2015-09-20 15:51:38 +02:00
|
|
|
/*
|
|
|
|
|
Using macros for everything to make linking simpler.
|
|
|
|
|
|
|
|
|
|
The big ones do bloat the executable.
|
2015-10-18 14:56:12 +02:00
|
|
|
|
|
|
|
|
## Calling convention
|
2015-09-20 15:51:38 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
.altmacro
|
|
|
|
|
|
2015-10-18 14:56:12 +02:00
|
|
|
.macro BEGIN
|
|
|
|
|
.code16
|
|
|
|
|
cli
|
|
|
|
|
/* Set %cs to 0. TODO Is that really needed? */ ;\
|
|
|
|
|
ljmp $0, $1f
|
|
|
|
|
1:
|
|
|
|
|
xor %ax, %ax
|
2015-10-17 00:03:05 +02:00
|
|
|
/* We must zero %ds for any data access. */ \
|
2015-10-18 14:56:12 +02:00
|
|
|
mov %ax, %ds
|
|
|
|
|
/* TODO is it really need to clear all those segment registers, e.g. for BIOS calls? */ \
|
|
|
|
|
mov %ax, %es
|
|
|
|
|
mov %ax, %fs
|
|
|
|
|
mov %ax, %gs
|
2015-09-20 20:37:12 +02:00
|
|
|
/* TODO What to move into BP and SP? http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process */ \
|
2015-10-18 14:56:12 +02:00
|
|
|
mov 0x0000, %bp
|
2015-10-17 00:03:05 +02:00
|
|
|
/* Automatically disables interrupts until the end of the next instruction. */ \
|
2015-10-18 14:56:12 +02:00
|
|
|
mov %ax, %ss
|
2015-09-20 20:37:12 +02:00
|
|
|
/* We should set SP because BIOS calls may depend on that. TODO confirm. */ \
|
|
|
|
|
mov %bp, %sp
|
2015-10-18 14:56:12 +02:00
|
|
|
.endm
|
2015-09-20 10:59:36 +02:00
|
|
|
|
2015-10-17 00:03:05 +02:00
|
|
|
/*
|
|
|
|
|
Load stage2 from disk to memory, and jump to it.
|
|
|
|
|
|
|
|
|
|
TODO not working.
|
|
|
|
|
|
|
|
|
|
To be used when the program does not fit in the 512 bytes.
|
|
|
|
|
|
|
|
|
|
Sample usage:
|
|
|
|
|
|
|
|
|
|
STAGE2
|
|
|
|
|
Stage 2 code here.
|
|
|
|
|
*/
|
2015-10-18 14:56:12 +02:00
|
|
|
.macro STAGE2
|
|
|
|
|
mov $2, %ah
|
|
|
|
|
/*
|
|
|
|
|
TODO get working on linker script.
|
|
|
|
|
Above my paygrade for now, so I just load a bunch of sectors instead.
|
|
|
|
|
*/
|
|
|
|
|
/* mov __stage2_size, %al;\ */
|
|
|
|
|
mov $9, %al
|
|
|
|
|
mov $0x80, %dl
|
|
|
|
|
mov $0, %ch
|
|
|
|
|
mov $0, %dh
|
|
|
|
|
mov $2, %cl
|
|
|
|
|
mov $1f, %bx
|
|
|
|
|
int $0x13
|
|
|
|
|
jmp 1f
|
|
|
|
|
.section .stage2
|
2015-10-17 00:03:05 +02:00
|
|
|
1:
|
2015-10-18 14:56:12 +02:00
|
|
|
.endm
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Enter protected mode.
|
|
|
|
|
|
|
|
|
|
Use the simplest GDT possible.
|
|
|
|
|
*/
|
|
|
|
|
.macro PROTECTED_MODE
|
2015-10-20 12:21:22 +02:00
|
|
|
/* Must come before they are used. */
|
|
|
|
|
.equ CODE_SEG, 8
|
|
|
|
|
.equ DATA_SEG, gdt_data - gdt_start
|
|
|
|
|
|
2015-10-18 14:56:12 +02:00
|
|
|
cli
|
|
|
|
|
/* Tell the processor where our Global Descriptor Table is in memory. */
|
|
|
|
|
lgdt gdt_descriptor
|
|
|
|
|
|
2015-10-20 12:21:22 +02:00
|
|
|
/*
|
|
|
|
|
Set PE (Protection Enable) bit in CR0 (Control Register 0),
|
|
|
|
|
effectively entering protected mode.
|
|
|
|
|
*/
|
2015-10-18 14:56:12 +02:00
|
|
|
mov %cr0, %eax
|
|
|
|
|
orl $0x1, %eax
|
|
|
|
|
mov %eax, %cr0
|
|
|
|
|
|
2015-10-20 12:21:22 +02:00
|
|
|
ljmp $CODE_SEG, $protected_mode
|
2015-10-18 14:56:12 +02:00
|
|
|
.code32
|
|
|
|
|
gdt_start:
|
|
|
|
|
gdt_null:
|
|
|
|
|
.long 0x0
|
|
|
|
|
.long 0x0
|
|
|
|
|
gdt_code:
|
|
|
|
|
.word 0xffff
|
|
|
|
|
.word 0x0
|
|
|
|
|
.byte 0x0
|
|
|
|
|
.byte 0b10011010
|
|
|
|
|
.byte 0b11001111
|
|
|
|
|
.byte 0x0
|
|
|
|
|
gdt_data:
|
|
|
|
|
.word 0xffff
|
|
|
|
|
.word 0x0
|
|
|
|
|
.byte 0x0
|
|
|
|
|
.byte 0b10010010
|
|
|
|
|
.byte 0b11001111
|
|
|
|
|
.byte 0x0
|
|
|
|
|
gdt_end:
|
|
|
|
|
gdt_descriptor:
|
2015-10-20 12:21:22 +02:00
|
|
|
.word gdt_end - gdt_start
|
|
|
|
|
.long gdt_start
|
|
|
|
|
vga_current_line:
|
|
|
|
|
.long 0
|
2015-10-18 14:56:12 +02:00
|
|
|
protected_mode:
|
2015-10-20 12:21:22 +02:00
|
|
|
/*
|
|
|
|
|
Setup the other segments.
|
|
|
|
|
Those movs are mandatory because they update the descriptor cache:
|
|
|
|
|
http://wiki.osdev.org/Descriptor_Cache
|
|
|
|
|
*/
|
2015-10-18 14:56:12 +02:00
|
|
|
mov $DATA_SEG, %ax
|
|
|
|
|
mov %ax, %ds
|
|
|
|
|
mov %ax, %es
|
|
|
|
|
mov %ax, %fs
|
|
|
|
|
mov %ax, %gs
|
|
|
|
|
mov %ax, %ss
|
2015-10-20 12:21:22 +02:00
|
|
|
/* TODO detect memory properly. */
|
|
|
|
|
mov $0X7000, %ebp
|
2015-10-18 14:56:12 +02:00
|
|
|
mov %ebp, %esp
|
|
|
|
|
.endm
|
2015-10-17 00:03:05 +02:00
|
|
|
|
|
|
|
|
/* BIOS */
|
|
|
|
|
|
2015-09-20 10:59:36 +02:00
|
|
|
#define CURSOR_POSITION(x, y) \
|
|
|
|
|
mov $0x02, %ah;\
|
|
|
|
|
mov $0x00, %bh;\
|
|
|
|
|
mov $0x ## x ## y, %dx;\
|
|
|
|
|
int $0x10
|
|
|
|
|
|
|
|
|
|
/* Clear the screen, move to position 0, 0. */
|
2015-10-18 14:56:12 +02:00
|
|
|
.macro CLEAR
|
|
|
|
|
mov $0x0600, %ax
|
|
|
|
|
mov $0x7, %bh
|
|
|
|
|
mov $0x0, %cx
|
|
|
|
|
mov $0x184f, %dx
|
|
|
|
|
int $0x10
|
2015-09-20 10:59:36 +02:00
|
|
|
CURSOR_POSITION(0, 0)
|
2015-10-18 14:56:12 +02:00
|
|
|
.endm
|
2015-09-20 10:59:36 +02:00
|
|
|
|
|
|
|
|
/*
|
2015-09-20 15:51:38 +02:00
|
|
|
Print a single immediate byte or 8 bit register.
|
2015-09-20 10:59:36 +02:00
|
|
|
|
|
|
|
|
`c` is it's value in hex.
|
2015-09-20 15:51:38 +02:00
|
|
|
|
|
|
|
|
Usage: character 'A' (ASCII 61):
|
|
|
|
|
|
|
|
|
|
PUTS(61)
|
2015-09-30 19:03:23 +02:00
|
|
|
|
|
|
|
|
Clobbers: ax
|
2015-09-20 10:59:36 +02:00
|
|
|
*/
|
|
|
|
|
#define PUTC(c) \
|
2015-09-20 15:51:38 +02:00
|
|
|
mov $0x0E, %ah;\
|
|
|
|
|
mov c, %al;\
|
|
|
|
|
int $0x10
|
|
|
|
|
|
2015-09-24 10:17:28 +02:00
|
|
|
/*
|
|
|
|
|
Convert a byte to hex ASCII value.
|
|
|
|
|
c: r/m8 byte to be converted
|
2015-10-20 12:21:22 +02:00
|
|
|
Output: two ASCII characters, is stored in `ah:al`
|
2015-09-24 10:17:28 +02:00
|
|
|
http://stackoverflow.com/questions/3853730/printing-hexadecimal-digits-with-assembly
|
|
|
|
|
*/
|
|
|
|
|
#define HEX(c) GAS_HEX c
|
|
|
|
|
.macro GAS_HEX c
|
|
|
|
|
mov \c, %al
|
2015-09-30 19:03:23 +02:00
|
|
|
mov \c, %ah
|
2015-09-24 10:17:28 +02:00
|
|
|
shr $4, %al
|
|
|
|
|
GAS_HEX_NIBBLE al
|
2015-09-30 19:03:23 +02:00
|
|
|
and $0x0F, %ah
|
|
|
|
|
GAS_HEX_NIBBLE ah
|
2015-09-24 10:17:28 +02:00
|
|
|
.endm
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Convert the low nibble of a r8 reg to ASCII of 8-bit in-place.
|
|
|
|
|
reg: r8 to be converted
|
2015-09-30 19:03:23 +02:00
|
|
|
Clobbered registers: none
|
2015-09-24 10:17:28 +02:00
|
|
|
Output: stored in reg itself. Letters are uppercase.
|
|
|
|
|
*/
|
|
|
|
|
.macro GAS_HEX_NIBBLE reg
|
|
|
|
|
LOCAL letter, end
|
|
|
|
|
cmp $10, %\reg
|
|
|
|
|
jae letter
|
|
|
|
|
/* 0x30 == '0' */
|
|
|
|
|
add $0x30, %\reg
|
|
|
|
|
jmp end
|
|
|
|
|
letter:
|
|
|
|
|
/* 0x37 == 'A' - 10 */
|
|
|
|
|
add $0x37, %\reg
|
|
|
|
|
end:
|
|
|
|
|
.endm
|
|
|
|
|
|
2015-09-30 19:03:23 +02:00
|
|
|
/*
|
|
|
|
|
Print a byte as two hexadecimal digits.
|
|
|
|
|
|
2015-10-06 12:27:24 +02:00
|
|
|
reg: 1 byte register.
|
|
|
|
|
|
2015-09-30 19:03:23 +02:00
|
|
|
Clobbers: ax, dl
|
|
|
|
|
*/
|
2015-10-20 21:04:49 +02:00
|
|
|
.macro PRINT_HEX reg
|
|
|
|
|
HEX(<\reg>)
|
|
|
|
|
mov %ah, %dl
|
|
|
|
|
PUTC(%al)
|
2015-09-30 19:03:23 +02:00
|
|
|
PUTC(%dl)
|
2015-10-20 21:04:49 +02:00
|
|
|
.endm
|
2015-09-29 23:55:54 +02:00
|
|
|
|
|
|
|
|
#define PRINT_NEWLINE \
|
|
|
|
|
PUTC($0x0A);\
|
|
|
|
|
PUTC($0x0D)
|
|
|
|
|
|
2015-09-20 15:51:38 +02:00
|
|
|
/*
|
|
|
|
|
Print a null terminated string.
|
|
|
|
|
|
|
|
|
|
Use as:
|
|
|
|
|
|
|
|
|
|
PRINT($s)
|
|
|
|
|
hlt
|
|
|
|
|
s:
|
|
|
|
|
.asciz "string"
|
|
|
|
|
|
|
|
|
|
We use this `cpp` macro to allow writing `PRINT(S)` with parenthesis.
|
|
|
|
|
*/
|
|
|
|
|
#define PRINT(s) GAS_PRINT s
|
|
|
|
|
.macro GAS_PRINT s
|
2015-09-24 10:17:28 +02:00
|
|
|
LOCAL loop, end
|
2015-09-20 15:51:38 +02:00
|
|
|
mov s, %si
|
|
|
|
|
mov $0x0e, %ah
|
|
|
|
|
loop:
|
|
|
|
|
lodsb
|
|
|
|
|
or %al, %al
|
2015-09-24 10:17:28 +02:00
|
|
|
jz end
|
2015-09-20 10:59:36 +02:00
|
|
|
int $0x10
|
2015-09-20 15:51:38 +02:00
|
|
|
jmp loop
|
2015-09-24 10:17:28 +02:00
|
|
|
end:
|
2015-09-20 15:51:38 +02:00
|
|
|
.endm
|
2015-10-06 12:27:24 +02:00
|
|
|
|
2015-10-17 00:03:05 +02:00
|
|
|
/* VGA */
|
2015-10-18 14:56:12 +02:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
Print a NULL terminated string to position 0 in VGA.
|
|
|
|
|
|
|
|
|
|
s: 32-bit register or memory containing the address of the string to print.
|
|
|
|
|
|
2015-10-20 12:21:22 +02:00
|
|
|
Clobbers: none.
|
|
|
|
|
|
|
|
|
|
Uses and updates vga_current_line to decide the current line.
|
|
|
|
|
Loops around the to the top.
|
2015-10-18 14:56:12 +02:00
|
|
|
*/
|
2015-10-20 12:21:22 +02:00
|
|
|
.macro VGA_PRINT_STRING s
|
2015-10-18 14:56:12 +02:00
|
|
|
LOCAL loop, end
|
2015-10-20 12:21:22 +02:00
|
|
|
push %eax
|
|
|
|
|
push %ebx
|
|
|
|
|
push %ecx
|
|
|
|
|
push %edx
|
|
|
|
|
mov \s, %ecx
|
|
|
|
|
mov vga_current_line, %eax
|
|
|
|
|
mov $0, %edx
|
|
|
|
|
/* Number of horizontal lines. */
|
|
|
|
|
mov $25, %ebx
|
|
|
|
|
div %ebx
|
|
|
|
|
mov %edx, %eax
|
|
|
|
|
/* 160 == 80 * 2 == line width * bytes per character on screen */
|
|
|
|
|
mov $160, %edx
|
|
|
|
|
mul %edx
|
|
|
|
|
/* 0xb8000 == magic video memory address which shows on the screen. */
|
|
|
|
|
lea 0xb8000(%eax), %edx
|
2015-10-18 14:56:12 +02:00
|
|
|
/* White on black. */
|
|
|
|
|
mov $0x0f, %ah
|
|
|
|
|
loop:
|
|
|
|
|
mov (%ecx), %al
|
|
|
|
|
cmp $0, %al
|
|
|
|
|
je end
|
|
|
|
|
mov %ax, (%edx)
|
|
|
|
|
add $1, %ecx
|
|
|
|
|
add $2, %edx
|
|
|
|
|
jmp loop
|
|
|
|
|
end:
|
2015-10-20 12:21:22 +02:00
|
|
|
incl vga_current_line
|
|
|
|
|
pop %edx
|
|
|
|
|
pop %ecx
|
|
|
|
|
pop %ebx
|
|
|
|
|
pop %eax
|
|
|
|
|
.endm
|
|
|
|
|
|
2015-10-20 21:00:24 +02:00
|
|
|
/*
|
|
|
|
|
Print a 32-bit register in hex.
|
|
|
|
|
|
|
|
|
|
Sample usage:
|
|
|
|
|
|
|
|
|
|
mov $12345678, %eax
|
|
|
|
|
VGA_PRINT_REG <%eax>
|
|
|
|
|
|
|
|
|
|
Expected output on screen:
|
|
|
|
|
|
|
|
|
|
12345678
|
|
|
|
|
*/
|
2015-10-20 12:21:22 +02:00
|
|
|
.macro VGA_PRINT_REG reg=<%eax>
|
|
|
|
|
LOCAL loop
|
|
|
|
|
push %eax
|
|
|
|
|
push %ebx
|
|
|
|
|
push %ecx
|
|
|
|
|
push %edx
|
|
|
|
|
/* Null terminator. */
|
|
|
|
|
mov \reg, %ecx
|
|
|
|
|
|
|
|
|
|
/* Write ASCII representation to memory. */
|
|
|
|
|
push $0
|
|
|
|
|
mov $2, %ebx
|
|
|
|
|
loop:
|
|
|
|
|
GAS_HEX <%cl>
|
|
|
|
|
mov %ax, %dx
|
|
|
|
|
shl $16, %edx
|
|
|
|
|
GAS_HEX <%ch>
|
|
|
|
|
mov %ax, %dx
|
|
|
|
|
push %edx
|
|
|
|
|
shr $16, %ecx
|
|
|
|
|
dec %ebx
|
|
|
|
|
cmp $0, %ebx
|
|
|
|
|
jne loop
|
|
|
|
|
|
|
|
|
|
/* Print it. */
|
|
|
|
|
mov %esp, %edx
|
|
|
|
|
VGA_PRINT_STRING <%edx>
|
|
|
|
|
|
|
|
|
|
/* Restore the stack. We have pushed 3 * 4 bytes. */
|
|
|
|
|
add $12, %esp
|
|
|
|
|
pop %edx
|
|
|
|
|
pop %ecx
|
|
|
|
|
pop %ebx
|
|
|
|
|
pop %eax
|
2015-10-18 14:56:12 +02:00
|
|
|
.endm
|