Uniformized macros: push save all registers, and use only GAS macros, no CPP

This commit is contained in:
Ciro Santilli
2015-10-21 15:55:27 +02:00
parent aaaea79a6f
commit 71e104bab0
31 changed files with 406 additions and 165 deletions

View File

@@ -56,6 +56,8 @@ Minimal operating systems to learn low level programming.
1. [UEFI](uefi/) 1. [UEFI](uefi/)
1. Misc 1. Misc
1. [hajji](hajji/) 1. [hajji](hajji/)
1. Tests
1. [PRINT_BYTES](test_print_bytes.S)
1. Theory 1. Theory
1. [Modes of operation](modes-of-operation.md) 1. [Modes of operation](modes-of-operation.md)
1. [Segmentation](segmentation.md) 1. [Segmentation](segmentation.md)

View File

@@ -114,4 +114,4 @@
- mouse - mouse
- DOS question: http://stackoverflow.com/questions/23043732/accessing-the-mouse-via-assembly-x86

View File

@@ -11,3 +11,15 @@ This is not meant provide a template from which you can write a real OS, but ins
Minimal examples are useful because it is easier to observe the requirements for a given concept to be observable. Minimal examples are useful because it is easier to observe the requirements for a given concept to be observable.
Another advantage is that it is easier to DRY up minimal examples (here done simply through `#include` and macros), which is much harder on progressive OS template tutorials, which tend to repeat big chunks of code between the examples. Another advantage is that it is easier to DRY up minimal examples (here done simply through `#include` and macros), which is much harder on progressive OS template tutorials, which tend to repeat big chunks of code between the examples.
## Pre-requisites
OS dev is one of the most insanely hard programming tasks a person can undertake, and will push your knowledge of several domains to the limit.
Knowing the following will help a lot:
- userland x86 assembly: https://github.com/cirosantilli/assembly-cheat
- compilation, linking and ELF format basics
- GDB debugging
While it is possible to learn those topics as you go along, and it is almost certain that you will end up learning more about them, we will not explain them here in detail.

View File

@@ -5,6 +5,6 @@ mov $0x0B, %ah
mov $0x0034, %bx mov $0x0034, %bx
int $0x10 int $0x10
PUTC($0x61) PUTC $'a
hlt hlt

View File

@@ -11,7 +11,7 @@ Carriage returns are needed just like in old days.
#include "common.h" #include "common.h"
BEGIN BEGIN
PRINT($msg) PRINT $msg
hlt hlt
msg: msg:
.asciz "hello\n\rworld" .asciz "hello\n\rworld"

View File

@@ -1,4 +1,11 @@
/* Clear screen by scrolling. */ /*
Clear screen by scrolling.
Expected output:
- "b" with red foreground at (0, 0)
- the entire screen background is green
*/
#include "common.h" #include "common.h"
BEGIN BEGIN
@@ -8,7 +15,7 @@ Print one 'a' char to ensure that something will be cleared.
On some systems, BIOS messages get automatically cleared. Not the case for QEMU 2.0.0. On some systems, BIOS messages get automatically cleared. Not the case for QEMU 2.0.0.
*/ */
PUTC($0x61) PUTC $'a
/* Scroll 0 is magic, and scrolls the entire selected rectangle. */ /* Scroll 0 is magic, and scrolls the entire selected rectangle. */
mov $0x0600, %ax mov $0x0600, %ax
@@ -29,7 +36,7 @@ Print a 'b' char to see where we are now.
TODO, on ThinkPad T400, the cursor gets put back to the initial position. But QEMU 2.0.0 leaves it in the middle ofthe screen. Thus we reset the position to make them work the same way. TODO, on ThinkPad T400, the cursor gets put back to the initial position. But QEMU 2.0.0 leaves it in the middle ofthe screen. Thus we reset the position to make them work the same way.
*/ */
CURSOR_POSITION(0, 0) CURSOR_POSITION
PUTC($0x62) PUTC $'b
hlt hlt

View File

@@ -1,6 +1,11 @@
/* /*
Write a character N times with given color. Write a character N times with given color.
Expected output: "bc", where:
- `b` has red foreground, and green background
- `c` has the default color (gray on black)
TODO: is this the only way? How to set the current color for ah = 0E? TODO: is this the only way? How to set the current color for ah = 0E?
Color codes: https://en.wikipedia.org/wiki/BIOS_color_attributes Color codes: https://en.wikipedia.org/wiki/BIOS_color_attributes
@@ -23,9 +28,9 @@ int $0x10
/* /*
The new color is reused only for character that overwrite the writen region. The new color is reused only for character that overwrite the writen region.
Cursor is not moved by the previous interrupt, so this produces a colored 'A'. Cursor is not moved by the previous interrupt, so this produces a colored 'a'.
*/ */
PUTC($0x62) PUTC $'b
PUTC($0x63) PUTC $'c
hlt hlt

View File

@@ -1,10 +1,16 @@
/*
Change the current cursor position.
Expected output: "cb"
*/
#include "common.h" #include "common.h"
BEGIN BEGIN
CLEAR CLEAR
/* Print "ab" */ /* Print "ab" */
PUTC($0x61) PUTC $'a
PUTC($0x62) PUTC $'b
/* Move back to 0, 0.*/ /* Move back to 0, 0.*/
mov $0x02, %ah mov $0x02, %ah
@@ -15,6 +21,6 @@ mov $0x0, %dx
int $0x10 int $0x10
/* Overwrite 'a' with c'. */ /* Overwrite 'a' with c'. */
PUTC($0x63) PUTC $'c
hlt hlt

View File

@@ -3,6 +3,8 @@
# int 15 # int 15
TODO Seems to be outputting trash :-)
http://wiki.osdev.org/Detecting_Memory_%28x86%29 http://wiki.osdev.org/Detecting_Memory_%28x86%29
Determine how much memory you've got, and how much of it is low memory. Determine how much memory you've got, and how much of it is low memory.
@@ -11,11 +13,21 @@ This is important in particular so that you can start your stack there
when you enter protected mode, since the stack grows down. when you enter protected mode, since the stack grows down.
In 16-bit mode, it does not matter much, In 16-bit mode, it does not matter much,
since most modern machines have all addressable memory there. since most modern machines have all addressable memory there,
but in 32-bit protected it does, as our emulator usually does not have all 4Gb.
And of course, 64-bit RAM is currently larger than the total RAM in the world.
`int 15` returns a list: `int 15` returns a list:
each time you call it a new memory region is returned. each time you call it a new memory region is returned.
The format is not too complicated, and documented at:
http://wiki.osdev.org/Detecting_Memory_%28x86%29#Detecting_Upper_Memory
- 8 bytes: base address of region.
- 8 bytes: length of region.
- 4 bytes: type or region. 1 for usable RAM.
- 4 bytes: some ACPI stuff that no one uses?
## Low memory ## Low memory
TODO what is it? TODO what is it?
@@ -23,7 +35,74 @@ TODO what is it?
#include "common.h" #include "common.h"
BEGIN BEGIN
mov $0xE820, %eax CLEAR
int $0x15 STAGE2
mov $output, %di
call do_e820
/* Debug aid. */
PRINT_WORD_HEX <%bp>
PRINT_NEWLINE
mov %bp, %ax
mov $0, %dx
/* Each entry is 24 bytes wide. */
mov $24, %cx
mul %cx
PRINT_BYTES $output, <%ax>
hlt hlt
/*
This was copy pasted from:
http://wiki.osdev.org/Detecting_Memory_%28x86%29#Getting_an_E820_Memory_Map
use the INT 0x15, eax= 0xE820 BIOS function to get a memory map
inputs: es:di -> destination buffer for 24 byte entries
outputs: bp = entry count, trashes all registers except esi
*/
do_e820:
xorl %ebx,%ebx # ebx must be 0 to start
xorw %bp,%bp # keep an entry count in bp
movl $0x0534D4150,%edx # Place "SMAP" into edx
movl $0xe820,%eax
movl $1, %es:20(%di)
movl $24,%ecx # ask for 24 bytes
int $0x15
jc do_e820.failed # carry set on first call means "unsupported function"
movl $0x0534D4150,%edx # Some BIOSes apparently trash this register?
cmpl %edx,%eax # on success, eax must have been reset to "SMAP"
jne do_e820.failed
testl %ebx,%ebx # ebx = 0 implies list is only 1 entry long (worthless)
je do_e820.failed
jmp do_e820.jmpin
do_e820.e820lp:
movl $0xe820,%eax # eax, ecx get trashed on every int 0x15 call
movl $1, %es:20(%di)
movl $24,%ecx # ask for 24 bytes again
int $0x15
jc do_e820.e820f # carry set means "end of list already reached"
movl $0x0534D4150,%edx # repair potentially trashed register
do_e820.jmpin:
jcxz do_e820.skipent # skip any 0 length entries
cmpb $20,%cl # got a 24 byte ACPI 3.X response?
jbe do_e820.notext
testb $1, %es:29(%di)
je do_e820.skipent
do_e820.notext:
mov %ecx, %es:8(%di)
or %ecx, %es:12(%di)
jz do_e820.skipent # if length uint64_t is 0, skip entry
incw %bp # got a good entry: ++count, move to next storage spot
addw $24,%di
do_e820.skipent:
testl %ebx,%ebx # if ebx resets to 0, list is complete
jne do_e820.e820lp
do_e820.e820f:
#movw %bp,mmap_ent # store the entry count
clc # there is "jc" on end of list to this point, so the carry must be cleared
ret
do_e820.failed:
stc # "function unsupported" error exit
ret
output:

View File

@@ -6,7 +6,7 @@
Load one more sector from the disk Load one more sector from the disk
besides the first 512 bytes and do something with it. besides the first 512 bytes and do something with it.
Expected outcome: `@` gets printed to the screen. Expected output: "a"
Grub 2.0 makes several calls to it under `grub-core/boot/i386/pc` Grub 2.0 makes several calls to it under `grub-core/boot/i386/pc`
@@ -81,7 +81,7 @@ just after the magic bytes.
*/ */
.section .stage2 .section .stage2
stage2: stage2:
PUTC($0x40) PUTC $'a
hlt hlt
/* /*

View File

@@ -4,9 +4,9 @@
BEGIN BEGIN
CLEAR CLEAR
STAGE2 STAGE2
PUTC($0x61) PUTC $'a
jmp sector3 jmp sector3
.org 512 .org 512
sector3: sector3:
PUTC($0x62) PUTC $'b
hlt hlt

View File

@@ -6,5 +6,5 @@ http://stackoverflow.com/questions/4113250/how-to-handle-keyboard-in-real-mode-t
BEGIN BEGIN
mov $0x00, %ah mov $0x00, %ah
int $0x16 int $0x16
PUTC(%al) PUTC <%al>
hlt hlt

View File

@@ -9,5 +9,5 @@ BEGIN
start: start:
mov $0x00, %ah mov $0x00, %ah
int $0x16 int $0x16
PUTC(%al) PUTC <%al>
jmp start jmp start

View File

@@ -11,7 +11,7 @@ Carriage returns are needed just like in old days.
#include "common.h" #include "common.h"
BEGIN BEGIN
PRINT($msg) PRINT $msg
hlt hlt
msg: msg:
.asciz "hello\nworld" .asciz "hello\nworld"

View File

@@ -37,7 +37,7 @@ which gets filled with the background color in bh.
BEGIN BEGIN
CLEAR CLEAR
PRINT($stair) PRINT $stair
/* Function ID. */ /* Function ID. */
mov $0x06, %ah mov $0x06, %ah

280
common.h
View File

@@ -1,39 +1,124 @@
/* /*
Using macros for everything to make linking simpler. Using macros for now instead of functions because it simplifies the linker script.
The big ones do bloat the executable. But the downsides are severe:
## Calling convention - no symbols to help debugging
- impossible to step over method calls: you have to step into everything
- larger output, supposing I can get linker gc for unused functions working,
see --gc-section, which is for now uncertain.
If I can get this working, I'll definitely move to function calls.
The problem is that if I don't, every image will need a stage 2 loader.
That is not too serious though, it could be added to BEGIN.
## Conventions
Every "function-like macro" should maintain register state
(flags currently not maintained).
%sp cannot be used to pass most arguments.
We don't care about setting %bp.
*/ */
/*
I really want this for the local labels.
The major downside is that every register passed as argument requires `<>`:
http://stackoverflow.com/questions/19776992/gas-altmacro-macro-with-a-percent-sign-in-a-default-parameter-fails-with-oper/
*/
.altmacro .altmacro
/* Helpers */
/* Push regiesters ax, bx, cx and dx. Lightweight `pusha`. */
.macro PUSH_ADX
push %ax
push %bx
push %cx
push %dx
.endm
/*
Pop registers dx, cx, bx, ax. Inverse order from PUSH_ADX,
so this cancels that one.
*/
.macro POP_DAX
pop %dx
pop %cx
pop %bx
pop %ax
.endm
/*
Convert the low nibble of a r8 reg to ASCII of 8-bit in-place.
reg: r8 to be converted
Output: stored in reg itself. Letters are uppercase.
*/
.macro HEX_NIBBLE reg
LOCAL letter, end
cmp $10, \reg
jae letter
add $'0, \reg
jmp end
letter:
/* 0x37 == 'A' - 10 */
add $0x37, \reg
end:
.endm
/*
Convert a byte to hex ASCII value.
c: r/m8 byte to be converted
Output: two ASCII characters, is stored in `ah:al`
http://stackoverflow.com/questions/3853730/printing-hexadecimal-digits-with-assembly
*/
.macro HEX c
mov \c, %al
mov \c, %ah
shr $4, %al
HEX_NIBBLE <%al>
and $0x0F, %ah
HEX_NIBBLE <%ah>
.endm
/* Structural. */
/*
Setup a sane initial state.
Should be the first thing in every file.
*/
.macro BEGIN .macro BEGIN
.code16 .code16
cli cli
/* Set %cs to 0. TODO Is that really needed? */ ;\ /* Set %cs to 0. TODO Is that really needed? */
ljmp $0, $1f ljmp $0, $1f
1: 1:
xor %ax, %ax xor %ax, %ax
/* We must zero %ds for any data access. */ \ /* We must zero %ds for any data access. */
mov %ax, %ds mov %ax, %ds
/* TODO is it really need to clear all those segment registers, e.g. for BIOS calls? */ \ /* TODO is it really need to clear all those segment registers, e.g. for BIOS calls? */
mov %ax, %es mov %ax, %es
mov %ax, %fs mov %ax, %fs
mov %ax, %gs mov %ax, %gs
/* TODO What to move into BP and SP? http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process */ \ /*
TODO What to move into BP and SP?
http://stackoverflow.com/questions/10598802/which-value-should-be-used-for-sp-for-booting-process
*/
mov 0x0000, %bp mov 0x0000, %bp
/* Automatically disables interrupts until the end of the next instruction. */ \ /* Automatically disables interrupts until the end of the next instruction. */
mov %ax, %ss mov %ax, %ss
/* We should set SP because BIOS calls may depend on that. TODO confirm. */ \ /* We should set SP because BIOS calls may depend on that. TODO confirm. */
mov %bp, %sp mov %bp, %sp
.endm .endm
/* /*
Load stage2 from disk to memory, and jump to it. 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. To be used when the program does not fit in the 512 bytes.
Sample usage: Sample usage:
@@ -44,7 +129,7 @@ Sample usage:
.macro STAGE2 .macro STAGE2
mov $2, %ah mov $2, %ah
/* /*
TODO get working on linker script. TODO get working with linker script.
Above my paygrade for now, so I just load a bunch of sectors instead. Above my paygrade for now, so I just load a bunch of sectors instead.
*/ */
/* mov __stage2_size, %al;\ */ /* mov __stage2_size, %al;\ */
@@ -83,7 +168,16 @@ Use the simplest GDT possible.
mov %eax, %cr0 mov %eax, %cr0
ljmp $CODE_SEG, $protected_mode ljmp $CODE_SEG, $protected_mode
.code32 /*
Our GDT contains:
- a null entry to fill the unusable entry 0:
http://stackoverflow.com/questions/33198282/why-have-the-first-segment-descriptor-of-the-global-descriptor-table-contain-onl
- a code and data. Both are necessary, because:
- it is impossible to write to the code segment
- it is impossible execute the data segment
Both start at 0 and span the entire memory,
allowing us to access anything without problems.
*/
gdt_start: gdt_start:
gdt_null: gdt_null:
.long 0x0 .long 0x0
@@ -108,6 +202,7 @@ gdt_descriptor:
.long gdt_start .long gdt_start
vga_current_line: vga_current_line:
.long 0 .long 0
.code32
protected_mode: protected_mode:
/* /*
Setup the other segments. Setup the other segments.
@@ -120,115 +215,103 @@ protected_mode:
mov %ax, %fs mov %ax, %fs
mov %ax, %gs mov %ax, %gs
mov %ax, %ss mov %ax, %ss
/* TODO detect memory properly. */ /*
TODO detect the last memory address available properly.
It depends on how much RAM we have.
*/
mov $0X7000, %ebp mov $0X7000, %ebp
mov %ebp, %esp mov %ebp, %esp
.endm .endm
/* BIOS */ /* BIOS */
#define CURSOR_POSITION(x, y) \ .macro CURSOR_POSITION x=$0, y=$0
mov $0x02, %ah;\ PUSH_ADX
mov $0x00, %bh;\ mov $0x02, %ah
mov $0x ## x ## y, %dx;\ mov $0x00, %bh
mov \x, %dh
mov \y, %dl
int $0x10 int $0x10
POP_DAX
.endm
/* Clear the screen, move to position 0, 0. */ /* Clear the screen, move to position 0, 0. */
.macro CLEAR .macro CLEAR
PUSH_ADX
mov $0x0600, %ax mov $0x0600, %ax
mov $0x7, %bh mov $0x7, %bh
mov $0x0, %cx mov $0x0, %cx
mov $0x184f, %dx mov $0x184f, %dx
int $0x10 int $0x10
CURSOR_POSITION(0, 0) CURSOR_POSITION
POP_DAX
.endm .endm
/* /*
Print a single immediate byte or 8 bit register. Print a 8 bit ASCII value at current cursor position.
`c` is it's value in hex. - c r/m/imm8 ASCII value to be printed.
Usage: character 'A' (ASCII 61): Usage:
PUTS(61) PUTC $'a
Clobbers: ax prints `'a'` to the screen.
*/ */
#define PUTC(c) \ .macro PUTC c=$0x20
mov $0x0E, %ah;\ push %ax
mov c, %al;\
int $0x10
/*
Convert a byte to hex ASCII value.
c: r/m8 byte to be converted
Output: two ASCII characters, is stored in `ah:al`
http://stackoverflow.com/questions/3853730/printing-hexadecimal-digits-with-assembly
*/
#define HEX(c) GAS_HEX c
.macro GAS_HEX c
mov \c, %al mov \c, %al
mov \c, %ah mov $0x0E, %ah
shr $4, %al int $0x10
GAS_HEX_NIBBLE al pop %ax
and $0x0F, %ah
GAS_HEX_NIBBLE ah
.endm
/*
Convert the low nibble of a r8 reg to ASCII of 8-bit in-place.
reg: r8 to be converted
Clobbered registers: none
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 .endm
/* /*
Print a byte as two hexadecimal digits. Print a byte as two hexadecimal digits.
reg: 1 byte register. - reg: 1 byte register.
Clobbers: ax, dl
*/ */
.macro PRINT_HEX reg .macro PRINT_HEX reg=<%al>
HEX(<\reg>) push %ax
mov %ah, %dl HEX <\reg>
PUTC(%al) PUTC <%al>
PUTC(%dl) PUTC <%ah>
pop %ax
.endm .endm
#define PRINT_NEWLINE \ /*
PUTC($0x0A);\ Print a 16-bit number
PUTC($0x0D)
- in: r/m/imm16
*/
.macro PRINT_WORD_HEX in=<%ax>
push %ax
mov \in, %ax
PRINT_HEX <%ah>
PRINT_HEX <%al>
pop %ax
.endm
.macro PRINT_NEWLINE
PUTC $'\n
PUTC $'\r
.endm
/* /*
Print a null terminated string. Print a null terminated string.
Use as: Use as:
PRINT($s) PRINT $s
hlt hlt
s: s:
.asciz "string" .asciz "string"
We use this `cpp` macro to allow writing `PRINT(S)` with parenthesis.
*/ */
#define PRINT(s) GAS_PRINT s .macro PRINT s
.macro GAS_PRINT s LOCAL end, loop
LOCAL loop, end
mov s, %si mov s, %si
mov $0x0e, %ah mov $0x0e, %ah
cld
loop: loop:
lodsb lodsb
or %al, %al or %al, %al
@@ -238,6 +321,43 @@ loop:
end: end:
.endm .endm
/*
Dump memory.
- s: starting address
- n: number of bytes to dump
*/
.macro PRINT_BYTES s, n=$16
LOCAL end, loop, no_newline
PUSH_ADX
push %di
mov s, %si
mov \n, %cx
mov $0, %di
cld
loop:
cmp $0, %cx
je end
dec %cx
lodsb
PRINT_HEX
PUTC
/* Print a newline for every 8 bytes. */
mov $0, %dx
mov %di, %ax
mov $8, %bx
div %bx
cmp $7, %dx
jne no_newline
PRINT_NEWLINE
no_newline:
inc %di
jmp loop
end:
pop %di
POP_DAX
.endm
/* VGA */ /* VGA */
/* /*
@@ -311,10 +431,10 @@ Expected output on screen:
push $0 push $0
mov $2, %ebx mov $2, %ebx
loop: loop:
GAS_HEX <%cl> HEX <%cl>
mov %ax, %dx mov %ax, %dx
shl $16, %edx shl $16, %edx
GAS_HEX <%ch> HEX <%ch>
mov %ax, %dx mov %ax, %dx
push %edx push %edx
shr $16, %ecx shr $16, %ecx

View File

@@ -7,7 +7,7 @@ Same as the kernel version.
#include "common.h" #include "common.h"
BEGIN BEGIN
start: start:
PUTC($0x61) PUTC $'a
mov $0xb6, %al mov $0xb6, %al
out %al, $0x43 out %al, $0x43

View File

@@ -103,7 +103,7 @@ BEGIN
mov %ah, %al mov %ah, %al
out %al, $0x40 out %al, $0x40
PUTC($0x61) PUTC $'a
sti sti
loop: loop:
@@ -111,7 +111,7 @@ loop:
jmp loop jmp loop
handler: handler:
PUTC($0x61) PUTC $'a
iret iret
/* TODO turn off. */ /* TODO turn off. */

View File

@@ -51,35 +51,35 @@ update_in_progress:
mov %al, %cl mov %al, %cl
PRINT_HEX <%al> PRINT_HEX <%al>
PUTC($0x20) PUTC
/* Minute. */ /* Minute. */
mov $0x02, %al mov $0x02, %al
out %al, $RTCaddress out %al, $RTCaddress
in $RTCdata, %al in $RTCdata, %al
PRINT_HEX <%al> PRINT_HEX <%al>
PUTC($0x20) PUTC
/* Hour. */ /* Hour. */
mov $0x04, %al mov $0x04, %al
out %al, $RTCaddress out %al, $RTCaddress
in $RTCdata, %al in $RTCdata, %al
PRINT_HEX <%al> PRINT_HEX <%al>
PUTC($0x20) PUTC
/* Day. */ /* Day. */
mov $0x07, %al mov $0x07, %al
out %al, $RTCaddress out %al, $RTCaddress
in $RTCdata, %al in $RTCdata, %al
PRINT_HEX <%al> PRINT_HEX <%al>
PUTC($0x20) PUTC
/* Month. */ /* Month. */
mov $0x08, %al mov $0x08, %al
out %al, $RTCaddress out %al, $RTCaddress
in $RTCdata, %al in $RTCdata, %al
PRINT_HEX <%al> PRINT_HEX <%al>
PUTC($0x20) PUTC
/* Year. */ /* Year. */
mov $0x09, %al mov $0x09, %al

View File

@@ -6,52 +6,35 @@ Could be done with GDB on the emulator, but this will also work on real hardware
#include "common.h" #include "common.h"
#define INITIAL_STORE(x) mov % ## x, x .macro INITIAL_STORE x
mov %\()\x, \x
.endm
#define INITIAL_DATA(x) \ .macro INITIAL_DATA x
x: .word 0;\ \x: .word 0
x ## s: .ascii #x " = \0" \x\()s: .ascii "\x = \0"
.endm
#define INITIAL_PRINT(x) \ .macro INITIAL_PRINT x
PRINT($x ## s);\ PRINT $\x\()s
PRINT_HEX <x>;\ PRINT_HEX <\x>
PRINT_NEWLINE PRINT_NEWLINE
.endm
INITIAL_STORE(ax) .irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
INITIAL_STORE(bx) INITIAL_STORE \reg
INITIAL_STORE(cx) .endr
INITIAL_STORE(dx)
INITIAL_STORE(cs)
INITIAL_STORE(ds)
INITIAL_STORE(es)
INITIAL_STORE(fs)
INITIAL_STORE(gs)
INITIAL_STORE(ss)
BEGIN BEGIN
STAGE2 STAGE2
INITIAL_PRINT(ax) .irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
INITIAL_PRINT(bx) INITIAL_PRINT \reg
INITIAL_PRINT(cx) .endr
INITIAL_PRINT(dx)
INITIAL_PRINT(cs)
INITIAL_PRINT(ds)
INITIAL_PRINT(es)
INITIAL_PRINT(fs)
INITIAL_PRINT(gs)
INITIAL_PRINT(ss)
hlt hlt
INITIAL_DATA(ax) .irp reg, bx, cx, dx, cs, ds, es, fs, gs, ss
INITIAL_DATA(bx) INITIAL_DATA \reg
INITIAL_DATA(cx) .endr
INITIAL_DATA(dx)
INITIAL_DATA(cs)
INITIAL_DATA(ds)
INITIAL_DATA(es)
INITIAL_DATA(fs)
INITIAL_DATA(gs)
INITIAL_DATA(ss)

View File

@@ -60,8 +60,8 @@ BEGIN
movw $handler, 0x00 movw $handler, 0x00
mov %cs, 0x02 mov %cs, 0x02
int $0 int $0
PUTC($0x62) PUTC $'b
hlt hlt
handler: handler:
PUTC($0x61) PUTC $'a
iret iret

View File

@@ -10,8 +10,8 @@ BEGIN
movw $handler, 0x04 movw $handler, 0x04
mov %cs, 0x06 mov %cs, 0x06
int $1 int $1
PUTC($0x62) PUTC $'b
hlt hlt
handler: handler:
PUTC($0x61) PUTC $'a
iret iret

View File

@@ -15,6 +15,6 @@ BEGIN
loop: loop:
jmp loop jmp loop
handler: handler:
PUTC($0x61) PUTC $'a
iret iret
jmp loop jmp loop

View File

@@ -8,5 +8,5 @@ BEGIN
mov %cs, 0x02 mov %cs, 0x02
int $0 int $0
handler: handler:
PUTC($0x61) PUTC $'a
int $0 int $0

View File

@@ -11,6 +11,6 @@ BEGIN
div %ax div %ax
jmp fail jmp fail
handler: handler:
PUTC($0x61) PUTC $'a
fail: fail:
hlt hlt

4
lidt.S
View File

@@ -26,7 +26,7 @@ BEGIN
mov %cs, 0x06 mov %cs, 0x06
int $0 int $0
PUTC($0x62) PUTC $'b
hlt hlt
idt: idt:
@@ -39,5 +39,5 @@ idt_descriptor:
.long idt .long idt
handler: handler:
PUTC($0x61) PUTC $'a
iret iret

View File

@@ -5,3 +5,5 @@ While NASM is a bit more convenient than GAS to write a boot sector, I think it
When writing an OS in C, we are going to use GCC, which already uses GAS. So it's better to reduce the number of assemblers to one and stick to GAS only. When writing an OS in C, we are going to use GCC, which already uses GAS. So it's better to reduce the number of assemblers to one and stick to GAS only.
Right now, this directory is not very DRY since NASM is secondary to me, so it contains mostly some copy / paste examples. Right now, this directory is not very DRY since NASM is secondary to me, so it contains mostly some copy / paste examples.
On top of that, GAS also supports other architectures besides x86, so learning it is more useful in that sense.

View File

@@ -81,34 +81,34 @@ BEGIN
mov $1, %ax mov $1, %ax
mov %ax, %ds mov %ax, %ds
mov %ds:msg, %al mov %ds:msg, %al
PUTC(%al) PUTC <%al>
/* /*
%ds is the default segment for GAS memory operations %ds is the default segment for GAS memory operations
if we don't write it explicitly. if we don't write it explicitly.
*/ */
mov msg, %al mov msg, %al
PUTC(%al) PUTC <%al>
mov $1, %ax mov $1, %ax
mov %ax, %es mov %ax, %es
mov %es:msg, %al mov %es:msg, %al
PUTC(%al) PUTC <%al>
mov $1, %ax mov $1, %ax
mov %ax, %fs mov %ax, %fs
mov %fs:msg, %al mov %fs:msg, %al
PUTC(%al) PUTC <%al>
mov $1, %ax mov $1, %ax
mov %ax, %gs mov %ax, %gs
mov %gs:msg, %al mov %gs:msg, %al
PUTC(%al) PUTC <%al>
mov $1, %ax mov $1, %ax
mov %ax, %ss mov %ax, %ss
mov %ss:msg, %al mov %ss:msg, %al
PUTC(%al) PUTC <%al>
hlt hlt
msg: msg:

View File

@@ -4,4 +4,4 @@
#include "common.h" #include "common.h"
BEGIN BEGIN
hlt hlt

17
test_print_bytes.S Normal file
View File

@@ -0,0 +1,17 @@
/*
Test PRINT_BYTES
Expected output:
40 41 42 43 44 45 46 47
48 49 4A 4B 4C 4D 4E 4F
50
*/
#include "common.h"
BEGIN
CLEAR
PRINT_BYTES $s, $17
hlt
s:
.ascii "@ABCDEFGHIJKLMNOP"

View File

@@ -2,6 +2,14 @@
Successor for BIOS. Successor for BIOS.
Made by Intel, mostly MIT open source, but vendors do modify it.
Matthew Garrett says it is huge: larger than Linux without drivers. Like BIOS, it is a "mini-OS".
Since it is huge, it inevitably contains bugs. Garret says that Intel sometimes does not feel like updating the firmware with bugfixes.
ARM is considering an implementation <https://wiki.linaro.org/ARM/UEFI>
TODO get a hello world program working: TODO get a hello world program working:
- http://www.rodsbooks.com/efi-programming/hello.html Best source so far: allowed me to compile the hello world! TODO: how to run it now on QEMU and real hardware? - http://www.rodsbooks.com/efi-programming/hello.html Best source so far: allowed me to compile the hello world! TODO: how to run it now on QEMU and real hardware?