replace text documentation with markdown

This commit is contained in:
2024-10-14 10:44:44 -04:00
parent ad2d37c63b
commit 933cccb88e
6 changed files with 230 additions and 301 deletions

230
docs/Stevia Notes.md Normal file
View File

@@ -0,0 +1,230 @@
# **Bootloader Documentation**
## **1. Calling Conventions**
### __cdecl16near Calling Convention
- **Purpose**: For calling near (within the same segment) functions in 16-bit code.
- **Stack Management**: Caller cleans up the stack after the function call.
- **Parameter Passing**: Parameters are pushed onto the stack from right to left.
- **Return Address**: A near return address (16-bit) is pushed onto the stack.
- **Return Value**: Placed in the AX register.
Example:
```assembly
; Caller
push param2
push param1
call near_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (near_func)
near_func:
push bp
mov bp, sp
; Function body
mov sp, bp
pop bp
ret
```
### __cdecl16far Calling Convention
- **Purpose**: For calling far (across different segments) functions in 16-bit code.
- **Stack Management**: Caller cleans up the stack after the function call.
- **Parameter Passing**: Parameters are pushed onto the stack from right to left.
- **Return Address**: A far return address (32-bit, consisting of a segment and an offset) is pushed onto the stack.
- **Return Value**: Placed in the AX register.
Example:
```assembly
; Caller
push param2
push param1
call far_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (far_func)
far_func:
push bp
mov bp, sp
; Function body
mov sp, bp
pop bp
retf ; Far return
```
### **Key Differences**
- **Return Address**: `__cdecl16near` uses a 16-bit return address; `__cdecl16far` uses a 32-bit return address (segment:offset).
- **Function Scope**: `__cdecl16near` is for functions within the same segment; `__cdecl16far` is for functions that may be in different segments.
- **Return Instruction**: `__cdecl16near` uses `ret`; `__cdecl16far` uses `retf` (far return).
### **Register Usage**
#### Caller-Saved (Volatile) Registers
- **AX**: Accumulator, often used for return values.
- **CX**: Counter register.
- **DX**: Data register, used for I/O operations.
- **SI/DI**: String operation indexes.
#### Callee-Saved (Non-Volatile) Registers
- **BP**: Base pointer, used for stack frame management.
- **SP**: Stack pointer.
- **BX**: Base register.
Example:
```assembly
; Caller
push param2
push param1
call near_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (near_func)
near_func:
push bp
mov bp, sp
; Save callee-saved registers if used
push bx
push si
push di
; Function body
; Use AX, CX, DX freely
; Restore callee-saved registers
pop di
pop si
pop bx
mov sp, bp
pop bp
ret
```
## **2. E820 Memory Map Usage**
### **Address Range Descriptor Structure**
| Offset | Name | Description |
|--------|---------------|---------------------------------------|
| 0 | BaseAddrLow | Low 32 bits of base address |
| 4 | BaseAddrHigh | High 32 bits of base address |
| 8 | LengthLow | Low 32 bits of length in bytes |
| 12 | LengthHigh | High 32 bits of length in bytes |
| 16 | Type | Address type of this range |
### **E820 Function Call**
#### Input
- **EAX**: Function code `E820h`.
- **EBX**: Continuation value for physical memory retrieval (0 for the first call).
- **ES:DI**: Buffer pointer to an Address Range Descriptor structure.
- **ECX**: Buffer size (minimum size 20 bytes).
- **EDX**: Signature ('SMAP').
#### Output
- **CF**: Carry flag (indicates success/failure).
- **EAX**: Signature ('SMAP').
- **ECX**: Buffer size (number of bytes returned).
- **EBX**: Continuation value for subsequent E820 calls.
### **Address Type Values**
| Value | Pneumonic | Description |
|-------|----------------------|-----------------------------------------------------------|
| 1 | AddressRangeMemory | Available RAM usable by the operating system. |
| 2 | AddressRangeReserved | Reserved by the system, unusable by the operating system. |
## **3. Example Calculations**
### **Partition Offset**
- Partition 1 offset = LBA 0x800 = 0x100000
- `bsSectorSize = 512`
### **First FAT Sector**
- `first_fat_sector = bsResSector = 32 => (32*512) = 0x4000`
- `first_fat_sector = 0x100000 + 0x4000 = 0x104000`
### **Total FAT Sectors**
- `total_fat_sectors = fat_sectors * number_of_FATs = 2001 * 2 = 4002`
- `total_fat_size = total_fat_sectors * bsSectorSize = 0x1F4400`
### **First Data Sector**
- `first_data_sector = FatStartSector + FatAreaSize = 0x104000 + 0x1F4400 = 0x2F8400`
### **FAT Table Look Up**
```c
if the cluster we got from the table entry was cluster 354
fat_sector = 354 / 128 = 2
fat_entry = 354 mod 128 = 98
so we load the 3rd (indexed from 0) fat table sector and read the 98th entry
```
Example:
```c
fat_table_offset = (first_fat_sector + 2) * 512
fat_table = *(fat_table_offset)
disk_read(fat_table[98])
```
## **4. Global Descriptor Table (GDT)**
### **Segment Attributes**
- **Pr**: Present bit (must be 1 for valid selectors).
- **Privl**: Privilege level (0 = kernel, 3 = user).
- **S**: Descriptor type (set for code/data segments, cleared for system segments).
- **Ex**: Executable bit (set if segment contains code).
- **DC**: Direction/Conforming bit (for data or code segments).
- **RW**: Readable/Writable (depends on segment type).
### **Granularity (Gr)**
- **Gr**: Granularity bit (0 = byte granularity, 1 = 4 KiB blocks).
- **Sz**: Size bit (0 = 16-bit mode, 1 = 32-bit mode).
### **GDT Entry Construction**
Each GDT entry is 8 bytes:
- First DWORD: Limit (0:15), Base (0:15)
- Second DWORD: Base (16:31), Attributes (8:12)
## **5. Memory Layout Example**
### **Low Memory (First MiB)**
| Start | End | Size | Type | Description |
|-------------|-------------|-----------------|-----------------------|----------------------------------|
| 0x00000000 | 0x000003FF | 1 KiB | RAM (partially unusable) | Real Mode IVT (Interrupt Vector Table) |
| 0x00000400 | 0x000004FF | 256 bytes | RAM (partially unusable) | BDA (BIOS data area) |
| 0x00000500 | 0x00007BFF | almost 30 KiB | RAM - free for use | Conventional memory |
| 0x00007C00 | 0x00007DFF | 512 bytes | RAM (partially unusable) | OS BootSector |
| 0x00007E00 | 0x0007FFFF | 480.5 KiB | RAM - free for use | Conventional memory |
| 0x00080000 | 0x0009FFFF | 128 KiB | RAM (partially unusable) | EBDA (Extended BIOS Data Area) |
| 0x000A0000 | 0x000FFFFF | 384 KiB | various (unusable) | Video memory, ROM Area |
### **Extended Memory (Above 1 MiB)**
| Start | End | Size | Description |
|-------------|-------------|-----------------|------------------------|
| 0x00100000 | 0x00EFFFFF | 14 MiB | RAM - free for use |
| 0x00F00000 | 0x00FFFFFF | 1 MiB | Possible memory-mapped hardware (ISA) |
| 0x01000000 | ? | ? | More extended memory |
| 0xC0000000 | 0xFFFFFFFF | 1 GiB | Memory mapped PCI devices, BIOS, etc. |
| 0x0000000100000000 | ? | ? | RAM - usable in PAE/64-bit mode |

View File

@@ -1,104 +0,0 @@
__cdecl16near Calling Convention
Purpose: Used for calling near (within the same segment) functions in 16-bit code.
Stack Management: The caller cleans up the stack after the function call.
Parameter Passing: Parameters are pushed onto the stack from right to left.
Return Address: A near return address (16-bit) is pushed onto the stack.
Return Value: The return value is typically placed in the AX register.
e.g
; Caller
push param2
push param1
call near_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (near_func)
near_func:
push bp
mov bp, sp
; Function body
mov sp, bp
pop bp
ret
__cdecl16far Calling Convention
Purpose: Used for calling far (across different segments) functions in 16-bit code.
Stack Management: The caller cleans up the stack after the function call.
Parameter Passing: Parameters are pushed onto the stack from right to left.
Return Address: A far return address (32-bit, consisting of a segment and an offset) is pushed onto the stack.
Return Value: The return value is typically placed in the AX register.
e.g
; Caller
push param2
push param1
call far_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (far_func)
far_func:
push bp
mov bp, sp
; Function body
mov sp, bp
pop bp
retf ; Far return
Key Differences
Return Address: __cdecl16near uses a 16-bit return address, while __cdecl16far uses a 32-bit return address (segment:offset).
Function Scope: __cdecl16near is for functions within the same segment, whereas __cdecl16far is for functions that may be in different segments.
Return Instruction: __cdecl16near uses ret, while __cdecl16far uses retf (far return).
### REGISTERS ###
Register Usage in __cdecl16near and __cdecl16far
Caller-Saved Registers (Volatile)
These registers must be saved by the caller if they wish to preserve their values across function calls:
AX: Accumulator register, often used for return values.
CX: Counter register, commonly used in loops and string operations.
DX: Data register, used for I/O operations and arithmetic.
SI: Source index for string operations.
DI: Destination index for string operations.
Callee-Saved Registers (Non-Volatile)
These registers must be preserved by the callee. If the callee uses these registers, it must save their original values and restore them before returning:
BP: Base pointer, used for stack frame management.
SP: Stack pointer, although typically managed by the calling convention itself.
BX: Base register, often used for addressing.
SI: Source index, if not used for string operations.
DI: Destination index, if not used for string operations.
e.g
; Caller
push param2
push param1
call near_func
add sp, 4 ; Clean up the stack (2 parameters * 2 bytes each)
; Callee (near_func)
near_func:
push bp
mov bp, sp
; Save callee-saved registers if used
push bx
push si
push di
; Function body
; Use AX, CX, DX freely
mov ax, [bp+4] ; Access first parameter
mov bx, [bp+6] ; Access second parameter
;
; your other code here
;
; Restore callee-saved registers
pop di
pop si
pop bx
mov sp, bp
pop bp
ret

View File

@@ -1,78 +0,0 @@
; Address Range Descriptor Structure
;
; Offset in Bytes Name Description
; 0 BaseAddrLow Low 32 Bits of Base Address
; 4 BaseAddrHigh High 32 Bits of Base Address
; 8 LengthLow Low 32 Bits of Length in Bytes
; 12 LengthHigh High 32 Bits of Length in Bytes
; 16 Type Address type of this range.
; Address Range Descriptor Structure
;
; Offset in Bytes Name Description
; 0 BaseAddrLow Low 32 Bits of Base Address
; 4 BaseAddrHigh High 32 Bits of Base Address
; 8 LengthLow Low 32 Bits of Length in Bytes
; 12 LengthHigh High 32 Bits of Length in Bytes
; 16 Type Address type of this range.
; Input:
;
; EAX Function Code E820h
; EBX Continuation Contains the "continuation value" to get the
; next run of physical memory. This is the
; value returned by a previous call to this
; routine. If this is the first call, EBX
; must contain zero.
; ES:DI Buffer Pointer Pointer to an Address Range Descriptor
; structure which the BIOS is to fill in.
; ECX Buffer Size The length in bytes of the structure passed
; to the BIOS. The BIOS will fill in at most
; ECX bytes of the structure or however much
; of the structure the BIOS implements. The
; minimum size which must be supported by both
; the BIOS and the caller is 20 bytes. Future
; implementations may extend this structure.
; EDX Signature 'SMAP' - Used by the BIOS to verify the
; caller is requesting the system map
; information to be returned in ES:DI.
;
; Output:
;
; CF Carry Flag Non-Carry - indicates no error
; EAX Signature 'SMAP' - Signature to verify correct BIOS
; revision.
; ES:DI Buffer Pointer Returned Address Range Descriptor pointer.
; Same value as on input.
; ECX Buffer Size Number of bytes returned by the BIOS in the
; address range descriptor. The minimum size
; structure returned by the BIOS is 20 bytes.
; EBX Continuation Contains the continuation value to get the
; next address descriptor. The actual
; significance of the continuation value is up
; to the discretion of the BIOS. The caller
; must pass the continuation value unchanged
; as input to the next iteration of the E820
; call in order to get the next Address Range
; Descriptor. A return value of zero means that
; this is the last descriptor
;
; Address Range Descriptor Structure
;
; Offset in Bytes Name Description
; 0 BaseAddrLow Low 32 Bits of Base Address
; 4 BaseAddrHigh High 32 Bits of Base Address
; 8 LengthLow Low 32 Bits of Length in Bytes
; 12 LengthHigh High 32 Bits of Length in Bytes
; 16 Type Address type of this range.
;
; The BaseAddrLow and BaseAddrHigh together are the 64 bit BaseAddress of this range.
; The BaseAddress is the physical address of the start of the range being specified.
;
; The LengthLow and LengthHigh together are the 64 bit Length of this range.
; The Length is the physical contiguous length in bytes of a range being specified.
;
; The Type field describes the usage of the described address range as defined in the table below.
; Value Pneumonic Description
; 1 AddressRangeMemory This run is available RAM usable by the operating system.
; 2 AddressRangeReserved This run of addresses is in use or reserved by the system, and must not be used by the operating system.
; Other Undefined Undefined - Reserved for future use.

View File

@@ -1,35 +0,0 @@
Partition 1 offset = LBA 0x800
= 0x100000
bsSectorSize = 512
first_fat_sector = bsResSector
= 32 => (32*512) = 0x4000
= 0x100000 + 0x4000
= 0x104000
total_fat_sectors = fat_sectors * number_of_FATs
= 2001 * 2
= 4002
total_fat_size = total_fat_sectors * bsSectorSize
= 0x1F4400
first_data_sector = FatStartSector + FatAreaSize
= 0x104000 + 0x1F4400
= 0x2F8400
FAT table look up
if the cluster we got from the table entry was cluster 354
fat_sector = 354 / 128
= 2
fat_entry = 354 mod 128
= 98
so we load the 3rd (indexed from 0) fat table sector and read the 98th entry
for the cluster chain.
// bad fake code below
fat_table_offset = (first_fat_sector + 2) * 512
fat_table = *(fat_table_offset)
disk_read(fat_table[98])

View File

@@ -1,61 +0,0 @@
GDT documentation below:
Pr: Present bit. This must be 1 for all valid selectors.
Privl: Privilege, 2 bits. Contains the ring level,
0 = highest (kernel), 3 = lowest (user applications).
S: Descriptor type. This bit should be set for code or data segments
and should be cleared for system segments (eg. a Task State Segment)
Ex: Executable bit. If 1 code in this segment can be executed
ie. a code selector. If 0 it is a data selector.
DC: Direction bit/Conforming bit.
Direction bit for data selectors: Tells the direction.
0 the segment grows up. 1 the segment grows down, ie. the offset has to be greater than the limit.
Conforming bit for code selectors:
If 1 code in this segment can be executed from an equal or lower privilege level.
For example, code in ring 3 can far-jump to conforming code in a ring 2 segment.
The privl-bits represent the highest privilege level that is allowed to execute the segment.
For example, code in ring 0 cannot far-jump to a conforming code segment with privl==0x2
while code in ring 2 and 3 can. Note that the privilege level remains the same
ie. a far-jump form ring 3 to a privl==2-segment remains in ring 3 after the jump.
If 0 code in this segment can only be executed from the ring set in privl.
RW: Readable bit/Writable bit.
Readable bit for code selectors: Whether read access for this segment is allowed. Write access is never allowed for code segments.
Writable bit for data selectors: Whether write access for this segment is allowed. Read access is always allowed for data segments.
Ac: Accessed bit. Just set to 0. The CPU sets this to 1 when the segment is accessed.
Gr: Granularity bit. If 0 the limit is in 1 B blocks (byte granularity), if 1 the limit is in 4 KiB blocks (page granularity).
Sz: Size bit. If 0 the selector defines 16 bit protected mode. If 1 it defines 32 bit protected mode.
You can have both 16 bit and 32 bit selectors at once.
AvL: Availible to software bit, the CPU does not use this field and software can read/write to it
D/B bit: The default operand-size bit is found in code-segment and data-segment descriptors but not in system-segment descriptors. Setting
this bit to 1 indicates a 32-bit default operand size, and clearing it indicates a 16-bit default size.
E bit: Expand down bit: Setting this bit to 1 identifies the data segment as expand-down.
In expand-down segments, the segment limit defines the lower segment boundary while the base is the upper boundary
A GDT entry is 8 bytes and is constructed as follows:
First DWORD
0-15 Limit 0:15 First 16 bits in the segment limiter
16-31 Base 0:15 First 16 bits in the base address
2nd DWORD
0:7 Base 16:23 Bits 16-23 in the base address
8:12 S/Type Segment type and attributes, S = bit 12, Type = 8:11, Type is either [1, DC, RW, Ac] <code> or [0, E, RW, Ac] <data>
13:14 Privl 0 = Highest privilege (OS), 3 = Lowest privilege (User applications)
15 Pr Set to 1 if segment is present
16:19 Limit 16:19 Bits 16-19 in the segment limiter
20:22 Attributes Different attributes, depending on the segment type
23 Gr Used together with the limiter, to determine the size of the segment
24:31 Base 24:31 The last 24-31 bits in the base address