Stage2
Posted: Sun Aug 28, 2011 8:27 pm
What's the problem with my code
Heres Stage2.asm
;Stage2.asm
bits 16
org 0x500
jmp Main
%include "GDT.inc"
bits 16
Print16:
pusha
.Loop1:
lodsb
or al, al
jz Puts16Done
mov ah, 0eh
int 10h
jmp .Loop1
Puts16Done:
popa
ret
bits 32
%define VIDMEM 0xB8000
%define COLS 80
%define LINES 25
%define CHAR_ATTRIB 63
_CurX db 0
_CurY db 0
Putch32:
pusha
mov edi, VIDMEM
xor eax, eax ; clear eax
mov ecx, COLS*2 ; Mode 7 has 2 bytes per char, so its COLS*2 bytes per line
mov al, byte [_CurY] ; get y pos
mul ecx ; multiply y*COLS
push eax ; save eax--the multiplication
;--------------------------------
; Now y * screen width is in eax. Now, just add _CurX. But, again remember that _CurX is relative
; to the current character count, not byte count. Because there are two bytes per character, we
; have to multiply _CurX by 2 first, then add it to our screen width * y.
;--------------------------------
mov al, byte [_CurX] ; multiply _CurX by 2 because it is 2 bytes per char
mov cl, 2
mul cl
pop ecx ; pop y*COLS result
add eax, ecx
;-------------------------------
; Now eax contains the offset address to draw the character at, so just add it to the base address
; of video memory (Stored in edi)
;-------------------------------
xor ecx, ecx
add edi, eax ; add it to the base address
;-------------------------------;
; Watch for new line ;
;-------------------------------;
cmp bl, 0x0A ; is it a newline character?
je .Row ; yep--go to next row
;-------------------------------;
; Print a character ;
;-------------------------------;
mov dl, bl ; Get character
mov dh, CHAR_ATTRIB ; the character attribute
mov word [edi], dx ; write to video display
;-------------------------------;
; Update next position ;
;-------------------------------;
inc byte [_CurX] ; go to next character
; cmp byte [_CurX], COLS ; are we at the end of the line?
; je .Row ; yep-go to next row
jmp .done ; nope, bail out
;-------------------------------;
; Go to next row ;
;-------------------------------;
.Row:
mov byte [_CurX], 0 ; go back to col 0
inc byte [_CurY] ; go to next row
;-------------------------------;
; Restore registers & return ;
;-------------------------------;
.done:
popa ; restore registers and return
ret
;**************************************************;
; Puts32 ()
; - Prints a null terminated string
; parm\ EBX = address of string to print
;**************************************************;
Print32:
;-------------------------------;
; Store registers ;
;-------------------------------;
pusha ; save registers
push ebx ; copy the string address
pop edi
.loop:
;-------------------------------;
; Get character ;
;-------------------------------;
mov bl, byte [edi] ; get next character
cmp bl, 0 ; is it 0 (Null terminator)?
je .done ; yep-bail out
;-------------------------------;
; Print the character ;
;-------------------------------;
call Putch32 ; Nope-print it out
;-------------------------------;
; Go to next character ;
;-------------------------------;
inc edi ; go to next character
jmp .loop
.done:
;-------------------------------;
; Update hardware cursor ;
;-------------------------------;
; Its more efficiant to update the cursor after displaying
; the complete string because direct VGA is slow
mov bh, byte [_CurY] ; get current position
mov bl, byte [_CurX]
call MovCur ; update cursor
popa ; restore registers, and return
ret
;**************************************************;
; MoveCur ()
; - Update hardware cursor
; parm/ bh = Y pos
; parm/ bl = x pos
;**************************************************;
bits 32
MovCur:
pusha ; save registers (aren't you getting tired of this comment?)
;-------------------------------;
; Get current position ;
;-------------------------------;
; Here, _CurX and _CurY are relitave to the current position on screen, not in memory.
; That is, we don't need to worry about the byte alignment we do when displaying characters,
; so just follow the forumla: location = _CurX + _CurY * COLS
xor eax, eax
mov ecx, COLS
mov al, bh ; get y pos
mul ecx ; multiply y*COLS
add al, bl ; Now add x
mov ebx, eax
;--------------------------------------;
; Set low byte index to VGA register ;
;--------------------------------------;
mov al, 0x0f
mov dx, 0x03D4
out dx, al
mov al, bl
mov dx, 0x03D5
out dx, al ; low byte
;---------------------------------------;
; Set high byte index to VGA register ;
;---------------------------------------;
xor eax, eax
mov al, 0x0e
mov dx, 0x03D4
out dx, al
mov al, bh
mov dx, 0x03D5
out dx, al ; high byte
popa
ret
;**************************************************;
; ClrScr32 ()
; - Clears screen
;**************************************************;
bits 32
ClrScr32:
pusha
cld
mov edi, VIDMEM
mov cx, 2000
mov ah, CHAR_ATTRIB
mov al, ' '
rep stosw
mov byte [_CurX], 0
mov byte [_CurY], 0
popa
ret
;**************************************************;
; GotoXY ()
; - Set current X/Y location
; parm\ AL=X position
; parm\ AH=Y position
;**************************************************;
bits 32
GotoXY:
pusha
mov [_CurX], al ; just set the current position
mov [_CurY], ah
popa
ret
bpbOEM db "My OS "
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xf0 ;; 0xF1
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY "
bsFileSystem: DB "FAT12 "
datasector dw 0x0000
cluster dw 0x0000
absoluteSector db 0x00
absoluteHead db 0x00
absoluteTrack db 0x00
;************************************************;
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;************************************************;
ClusterLBA:
sub ax, 0x0002 ; zero base cluster number
xor cx, cx
mov cl, BYTE [bpbSectorsPerCluster] ; convert byte to word
mul cx
add ax, WORD [datasector] ; base data sector
ret
;************************************************;
; Convert LBA to CHS
; AX=>LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;
;************************************************;
LBACHS:
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbSectorsPerTrack] ; calculate
inc dl ; adjust for sector 0
mov BYTE [absoluteSector], dl
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbHeadsPerCylinder] ; calculate
mov BYTE [absoluteHead], dl
mov BYTE [absoluteTrack], al
ret
;************************************************;
; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:EBX=>Buffer to read to
;************************************************;
ReadSectors:
.MAIN:
mov di, 0x0005 ; five retries for error
.SECTORLOOP:
push ax
push bx
push cx
call LBACHS ; convert starting sector to CHS
mov ah, 0x02 ; BIOS read sector
mov al, 0x01 ; read one sector
mov ch, BYTE [absoluteTrack] ; track
mov cl, BYTE [absoluteSector] ; sector
mov dh, BYTE [absoluteHead] ; head
mov dl, BYTE [bsDriveNumber] ; drive
int 0x13 ; invoke BIOS
jnc .SUCCESS ; test for read error
xor ax, ax ; BIOS reset disk
int 0x13 ; invoke BIOS
dec di ; decrement error counter
pop cx
pop bx
pop ax
jnz .SECTORLOOP ; attempt to read again
int 0x18
.SUCCESS:
pop cx
pop bx
pop ax
add bx, WORD [bpbBytesPerSector] ; queue next buffer
inc ax ; queue next sector
loop .MAIN ; read next sector
ret
%define ROOT_OFFSET 0x2e00
%define FAT_SEG 0x2c0
%define ROOT_SEG 0x2e0
;*******************************************
; LoadRoot ()
; - Load Root Directory Table to 0x7e00
;*******************************************
LoadRoot:
pusha ; store registers
push es
; compute size of root directory and store in "cx"
xor cx, cx ; clear registers
xor dx, dx
mov ax, 32 ; 32 byte directory entry
mul WORD [bpbRootEntries] ; total size of directory
div WORD [bpbBytesPerSector] ; sectors used by directory
xchg ax, cx ; move into AX
; compute location of root directory and store in "ax"
mov al, BYTE [bpbNumberOfFATs] ; number of FATs
mul WORD [bpbSectorsPerFAT] ; sectors used by FATs
add ax, WORD [bpbReservedSectors]
mov WORD [datasector], ax ; base of root directory
add WORD [datasector], cx
; read root directory into 0x7e00
push word ROOT_SEG
pop es
mov bx, 0 ; copy root dir
call ReadSectors ; read in directory table
pop es
popa ; restore registers and return
ret
;*******************************************
; LoadFAT ()
; - Loads FAT table to 0x7c00
;
; Parm/ ES:DI => Root Directory Table
;*******************************************
LoadFAT:
pusha ; store registers
push es
; compute size of FAT and store in "cx"
xor ax, ax
mov al, BYTE [bpbNumberOfFATs] ; number of FATs
mul WORD [bpbSectorsPerFAT] ; sectors used by FATs
mov cx, ax
; compute location of FAT and store in "ax"
mov ax, WORD [bpbReservedSectors]
; read FAT into memory (Overwrite our bootloader at 0x7c00)
push word FAT_SEG
pop es
xor bx, bx
call ReadSectors
pop es
popa ; restore registers and return
ret
;*******************************************
; FindFile ()
; - Search for filename in root table
;
; parm/ DS:SI => File name
; ret/ AX => File index number in directory table. -1 if error
;*******************************************
FindFile:
push cx ; store registers
push dx
push bx
mov bx, si ; copy filename for later
; browse root directory for binary image
mov cx, WORD [bpbRootEntries] ; load loop counter
mov di, ROOT_OFFSET ; locate first root entry at 1 MB mark
cld ; clear direction flag
.LOOP:
push cx
mov cx, 11 ; eleven character name. Image name is in SI
mov si, bx ; image name is in BX
push di
rep cmpsb ; test for entry match
pop di
je .Found
pop cx
add di, 32 ; queue next directory entry
loop .LOOP
.NotFound:
pop bx ; restore registers and return
pop dx
pop cx
mov ax, -1 ; set error code
ret
.Found:
pop ax ; return value into AX contains entry of file
pop bx ; restore registers and return
pop dx
pop cx
ret
LoadFile:
xor ecx, ecx ; size of file in sectors
push ecx
.FIND_FILE:
push bx ; BX=>BP points to buffer to write to; store it for later
push bp
call FindFile ; find our file. ES:SI contains our filename
cmp ax, -1
jne .LOAD_IMAGE_PRE
pop bp
pop bx
pop ecx
mov ax, -1
ret
.LOAD_IMAGE_PRE:
sub edi, ROOT_OFFSET
sub eax, ROOT_OFFSET
; get starting cluster
push word ROOT_SEG ;root segment loc
pop es
mov dx, WORD [es:di + 0x001A]; DI points to file entry in root directory table. Refrence the table...
mov WORD [cluster], dx ; file's first cluster
pop bx ; get location to write to so we dont screw up the stack
pop es
push bx ; store location for later again
push es
call LoadFAT
.LOAD_IMAGE:
; load the cluster
mov ax, WORD [cluster] ; cluster to read
pop es ; bx:bp=es:bx
pop bx
call ClusterLBA
xor cx, cx
mov cl, BYTE [bpbSectorsPerCluster]
call ReadSectors
pop ecx
inc ecx ; add one more sector to counter
push ecx
push bx
push es
mov ax, FAT_SEG ;start reading from fat
mov es, ax
xor bx, bx
; get next cluster
mov ax, WORD [cluster] ; identify current cluster
mov cx, ax ; copy current cluster
mov dx, ax
shr dx, 0x0001 ; divide by two
add cx, dx ; sum for (3/2)
mov bx, 0 ;location of fat in memory
add bx, cx
mov dx, WORD [es:bx]
test ax, 0x0001 ; test for odd or even cluster
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
and dx, 0000111111111111b ; take low 12 bits
jmp .DONE
.ODD_CLUSTER:
shr dx, 0x0004 ; take high 12 bits
.DONE:
mov WORD [cluster], dx
cmp dx, 0x0ff0 ; test for end of file marker
jb .LOAD_IMAGE
.SUCCESS:
pop es
pop bx
pop ecx
xor ax, ax
ret
%define PMODE_BASE 0x100000
%define RMODE_BASE 0x3000
ImageName db "KRNL SYS"
ImageSize db 0
EnableA20_KKbrd:
cli
push ax
mov al, 0xdd ; send enable a20 address line command to controller
out 0x64, al
pop ax
ret
;--------------------------------------------
; Enables a20 line through output port
;--------------------------------------------
EnableA20_KKbrd_Out:
cli
pusha
call wait_input
mov al,0xAD
out 0x64,al ; disable keyboard
call wait_input
mov al,0xD0
out 0x64,al ; tell controller to read output port
call wait_output
in al,0x60
push eax ; get output port data and store it
call wait_input
mov al,0xD1
out 0x64,al ; tell controller to write output port
call wait_input
pop eax
or al,2 ; set bit 1 (enable a20)
out 0x60,al ; write out data back to the output port
call wait_input
mov al,0xAE ; enable keyboard
out 0x64,al
call wait_input
popa
sti
ret
; wait for input buffer to be clear
wait_input:
in al,0x64
test al,2
jnz wait_input
ret
; wait for output buffer to be clear
wait_output:
in al,0x64
test al,1
jz wait_output
ret
;--------------------------------------
; Enables a20 line through bios
;--------------------------------------
EnableA20_Bios:
pusha
mov ax, 0x2401
int 0x15
popa
ret
;-------------------------------------------------
; Enables a20 line through system control port A
;-------------------------------------------------
EnableA20_SysControlA:
push ax
mov al, 2
out 0x92, al
pop ax
ret
Booting db "Loading Kernel...", 0x0A, 0x0D, 0
Failure db "FATAL ERROR: KRNL.SYS does not exist...", 0x0A, 0x0D, 0
Main:
mov si, Booting
call Print16
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0x0
mov ss, ax
mov sp, 0xFFFF
sti
SetUp:
call InstallGDT
call EnableA20_KKbrd_Out
call LoadRoot
mov ebx, 0
mov bp, RMODE_BASE
mov si, ImageName
call LoadFile
mov dword [ImageSize], ecx
cmp ax, 0
je StartStage3
mov si, Failure
call Print16
mov ah, 0
int 16
int 19
cli
hlt
StartStage3:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_DESC:Stage3
bits 32
Stage3:
mov ax, DATA_DESC
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
CopyImage:
mov eax, dword [ImageSize]
movzx ebx, word [bpbBytesPerSector]
mul ebx
mov ebx, 4
div ebx
cld
mov esi, RMODE_BASE
mov edi, PMODE_BASE
mov ecx, eax
rep movsd
jmp CODE_DESC:PMODE_BASE
cli
hlt
here's GDT.inc
%ifndef __GDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __GDT_INC_67343546FDCC56AAB872_INCLUDED__
bits 16
InstallGDT:
cli
pusha
lgdt [toc]
sti
popa
ret
gdt_data:
dd 0
dd 0
; gdt code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
; gdt data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
end_of_gdt:
toc:
dw end_of_gdt - gdt_data - 1
dd gdt_data
%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10
%endif ;__GDT_INC_67343546FDCC56AAB872_INCLUDED__
Here's the build.sh
if test "`whoami`" != "root" ; then
echo "You must be logged in as root to build (for loopback mounting)"
echo "Enter 'su' or 'sudo bash' to switch to root"
exit
fi
if [ ! -e floppy.img ]
then
echo ">>> Creating new floppy image..."
mkdosfs -C floppy.img 1440 || exit
fi
echo ">>> Assembling bootloader..."
nasm Stage1/Stage1.asm -o Stage1.bin || exit
echo ">>> Assembling kernel loader..."
cd Stage2
nasm Stage2.asm -o KRNLDR.SYS || exit
cd
cd Desktop/MyOS
echo ">>> Assembling kernel..."
nasm Kernel/kernel.asm -o KRNL.SYS || exit
echo ">>> Adding bootloader to floppy image..."
dd status=noxfer conv=notrunc if=Stage1.bin of=floppy.img || exit
echo ">>> Copying kernel..."
rm -rf tmp-loop
mkdir tmp-loop && mount -o loop -t vfat floppy.img tmp-loop && cp Stage2/KRNLDR.SYS tmp-loop/ && cp KRNL.SYS tmp-loop/ && cp Stage2/GDT.inc tmp-loop
umount tmp-loop || exit
rm -rf tmp-loop || exit
rm -rf tmp-loop
echo '>>> Done!'
Heres Stage2.asm
;Stage2.asm
bits 16
org 0x500
jmp Main
%include "GDT.inc"
bits 16
Print16:
pusha
.Loop1:
lodsb
or al, al
jz Puts16Done
mov ah, 0eh
int 10h
jmp .Loop1
Puts16Done:
popa
ret
bits 32
%define VIDMEM 0xB8000
%define COLS 80
%define LINES 25
%define CHAR_ATTRIB 63
_CurX db 0
_CurY db 0
Putch32:
pusha
mov edi, VIDMEM
xor eax, eax ; clear eax
mov ecx, COLS*2 ; Mode 7 has 2 bytes per char, so its COLS*2 bytes per line
mov al, byte [_CurY] ; get y pos
mul ecx ; multiply y*COLS
push eax ; save eax--the multiplication
;--------------------------------
; Now y * screen width is in eax. Now, just add _CurX. But, again remember that _CurX is relative
; to the current character count, not byte count. Because there are two bytes per character, we
; have to multiply _CurX by 2 first, then add it to our screen width * y.
;--------------------------------
mov al, byte [_CurX] ; multiply _CurX by 2 because it is 2 bytes per char
mov cl, 2
mul cl
pop ecx ; pop y*COLS result
add eax, ecx
;-------------------------------
; Now eax contains the offset address to draw the character at, so just add it to the base address
; of video memory (Stored in edi)
;-------------------------------
xor ecx, ecx
add edi, eax ; add it to the base address
;-------------------------------;
; Watch for new line ;
;-------------------------------;
cmp bl, 0x0A ; is it a newline character?
je .Row ; yep--go to next row
;-------------------------------;
; Print a character ;
;-------------------------------;
mov dl, bl ; Get character
mov dh, CHAR_ATTRIB ; the character attribute
mov word [edi], dx ; write to video display
;-------------------------------;
; Update next position ;
;-------------------------------;
inc byte [_CurX] ; go to next character
; cmp byte [_CurX], COLS ; are we at the end of the line?
; je .Row ; yep-go to next row
jmp .done ; nope, bail out
;-------------------------------;
; Go to next row ;
;-------------------------------;
.Row:
mov byte [_CurX], 0 ; go back to col 0
inc byte [_CurY] ; go to next row
;-------------------------------;
; Restore registers & return ;
;-------------------------------;
.done:
popa ; restore registers and return
ret
;**************************************************;
; Puts32 ()
; - Prints a null terminated string
; parm\ EBX = address of string to print
;**************************************************;
Print32:
;-------------------------------;
; Store registers ;
;-------------------------------;
pusha ; save registers
push ebx ; copy the string address
pop edi
.loop:
;-------------------------------;
; Get character ;
;-------------------------------;
mov bl, byte [edi] ; get next character
cmp bl, 0 ; is it 0 (Null terminator)?
je .done ; yep-bail out
;-------------------------------;
; Print the character ;
;-------------------------------;
call Putch32 ; Nope-print it out
;-------------------------------;
; Go to next character ;
;-------------------------------;
inc edi ; go to next character
jmp .loop
.done:
;-------------------------------;
; Update hardware cursor ;
;-------------------------------;
; Its more efficiant to update the cursor after displaying
; the complete string because direct VGA is slow
mov bh, byte [_CurY] ; get current position
mov bl, byte [_CurX]
call MovCur ; update cursor
popa ; restore registers, and return
ret
;**************************************************;
; MoveCur ()
; - Update hardware cursor
; parm/ bh = Y pos
; parm/ bl = x pos
;**************************************************;
bits 32
MovCur:
pusha ; save registers (aren't you getting tired of this comment?)
;-------------------------------;
; Get current position ;
;-------------------------------;
; Here, _CurX and _CurY are relitave to the current position on screen, not in memory.
; That is, we don't need to worry about the byte alignment we do when displaying characters,
; so just follow the forumla: location = _CurX + _CurY * COLS
xor eax, eax
mov ecx, COLS
mov al, bh ; get y pos
mul ecx ; multiply y*COLS
add al, bl ; Now add x
mov ebx, eax
;--------------------------------------;
; Set low byte index to VGA register ;
;--------------------------------------;
mov al, 0x0f
mov dx, 0x03D4
out dx, al
mov al, bl
mov dx, 0x03D5
out dx, al ; low byte
;---------------------------------------;
; Set high byte index to VGA register ;
;---------------------------------------;
xor eax, eax
mov al, 0x0e
mov dx, 0x03D4
out dx, al
mov al, bh
mov dx, 0x03D5
out dx, al ; high byte
popa
ret
;**************************************************;
; ClrScr32 ()
; - Clears screen
;**************************************************;
bits 32
ClrScr32:
pusha
cld
mov edi, VIDMEM
mov cx, 2000
mov ah, CHAR_ATTRIB
mov al, ' '
rep stosw
mov byte [_CurX], 0
mov byte [_CurY], 0
popa
ret
;**************************************************;
; GotoXY ()
; - Set current X/Y location
; parm\ AL=X position
; parm\ AH=Y position
;**************************************************;
bits 32
GotoXY:
pusha
mov [_CurX], al ; just set the current position
mov [_CurY], ah
popa
ret
bpbOEM db "My OS "
bpbBytesPerSector: DW 512
bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1
bpbNumberOfFATs: DB 2
bpbRootEntries: DW 224
bpbTotalSectors: DW 2880
bpbMedia: DB 0xf0 ;; 0xF1
bpbSectorsPerFAT: DW 9
bpbSectorsPerTrack: DW 18
bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0
bsDriveNumber: DB 0
bsUnused: DB 0
bsExtBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3
bsVolumeLabel: DB "MOS FLOPPY "
bsFileSystem: DB "FAT12 "
datasector dw 0x0000
cluster dw 0x0000
absoluteSector db 0x00
absoluteHead db 0x00
absoluteTrack db 0x00
;************************************************;
; Convert CHS to LBA
; LBA = (cluster - 2) * sectors per cluster
;************************************************;
ClusterLBA:
sub ax, 0x0002 ; zero base cluster number
xor cx, cx
mov cl, BYTE [bpbSectorsPerCluster] ; convert byte to word
mul cx
add ax, WORD [datasector] ; base data sector
ret
;************************************************;
; Convert LBA to CHS
; AX=>LBA Address to convert
;
; absolute sector = (logical sector / sectors per track) + 1
; absolute head = (logical sector / sectors per track) MOD number of heads
; absolute track = logical sector / (sectors per track * number of heads)
;
;************************************************;
LBACHS:
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbSectorsPerTrack] ; calculate
inc dl ; adjust for sector 0
mov BYTE [absoluteSector], dl
xor dx, dx ; prepare dx:ax for operation
div WORD [bpbHeadsPerCylinder] ; calculate
mov BYTE [absoluteHead], dl
mov BYTE [absoluteTrack], al
ret
;************************************************;
; Reads a series of sectors
; CX=>Number of sectors to read
; AX=>Starting sector
; ES:EBX=>Buffer to read to
;************************************************;
ReadSectors:
.MAIN:
mov di, 0x0005 ; five retries for error
.SECTORLOOP:
push ax
push bx
push cx
call LBACHS ; convert starting sector to CHS
mov ah, 0x02 ; BIOS read sector
mov al, 0x01 ; read one sector
mov ch, BYTE [absoluteTrack] ; track
mov cl, BYTE [absoluteSector] ; sector
mov dh, BYTE [absoluteHead] ; head
mov dl, BYTE [bsDriveNumber] ; drive
int 0x13 ; invoke BIOS
jnc .SUCCESS ; test for read error
xor ax, ax ; BIOS reset disk
int 0x13 ; invoke BIOS
dec di ; decrement error counter
pop cx
pop bx
pop ax
jnz .SECTORLOOP ; attempt to read again
int 0x18
.SUCCESS:
pop cx
pop bx
pop ax
add bx, WORD [bpbBytesPerSector] ; queue next buffer
inc ax ; queue next sector
loop .MAIN ; read next sector
ret
%define ROOT_OFFSET 0x2e00
%define FAT_SEG 0x2c0
%define ROOT_SEG 0x2e0
;*******************************************
; LoadRoot ()
; - Load Root Directory Table to 0x7e00
;*******************************************
LoadRoot:
pusha ; store registers
push es
; compute size of root directory and store in "cx"
xor cx, cx ; clear registers
xor dx, dx
mov ax, 32 ; 32 byte directory entry
mul WORD [bpbRootEntries] ; total size of directory
div WORD [bpbBytesPerSector] ; sectors used by directory
xchg ax, cx ; move into AX
; compute location of root directory and store in "ax"
mov al, BYTE [bpbNumberOfFATs] ; number of FATs
mul WORD [bpbSectorsPerFAT] ; sectors used by FATs
add ax, WORD [bpbReservedSectors]
mov WORD [datasector], ax ; base of root directory
add WORD [datasector], cx
; read root directory into 0x7e00
push word ROOT_SEG
pop es
mov bx, 0 ; copy root dir
call ReadSectors ; read in directory table
pop es
popa ; restore registers and return
ret
;*******************************************
; LoadFAT ()
; - Loads FAT table to 0x7c00
;
; Parm/ ES:DI => Root Directory Table
;*******************************************
LoadFAT:
pusha ; store registers
push es
; compute size of FAT and store in "cx"
xor ax, ax
mov al, BYTE [bpbNumberOfFATs] ; number of FATs
mul WORD [bpbSectorsPerFAT] ; sectors used by FATs
mov cx, ax
; compute location of FAT and store in "ax"
mov ax, WORD [bpbReservedSectors]
; read FAT into memory (Overwrite our bootloader at 0x7c00)
push word FAT_SEG
pop es
xor bx, bx
call ReadSectors
pop es
popa ; restore registers and return
ret
;*******************************************
; FindFile ()
; - Search for filename in root table
;
; parm/ DS:SI => File name
; ret/ AX => File index number in directory table. -1 if error
;*******************************************
FindFile:
push cx ; store registers
push dx
push bx
mov bx, si ; copy filename for later
; browse root directory for binary image
mov cx, WORD [bpbRootEntries] ; load loop counter
mov di, ROOT_OFFSET ; locate first root entry at 1 MB mark
cld ; clear direction flag
.LOOP:
push cx
mov cx, 11 ; eleven character name. Image name is in SI
mov si, bx ; image name is in BX
push di
rep cmpsb ; test for entry match
pop di
je .Found
pop cx
add di, 32 ; queue next directory entry
loop .LOOP
.NotFound:
pop bx ; restore registers and return
pop dx
pop cx
mov ax, -1 ; set error code
ret
.Found:
pop ax ; return value into AX contains entry of file
pop bx ; restore registers and return
pop dx
pop cx
ret
LoadFile:
xor ecx, ecx ; size of file in sectors
push ecx
.FIND_FILE:
push bx ; BX=>BP points to buffer to write to; store it for later
push bp
call FindFile ; find our file. ES:SI contains our filename
cmp ax, -1
jne .LOAD_IMAGE_PRE
pop bp
pop bx
pop ecx
mov ax, -1
ret
.LOAD_IMAGE_PRE:
sub edi, ROOT_OFFSET
sub eax, ROOT_OFFSET
; get starting cluster
push word ROOT_SEG ;root segment loc
pop es
mov dx, WORD [es:di + 0x001A]; DI points to file entry in root directory table. Refrence the table...
mov WORD [cluster], dx ; file's first cluster
pop bx ; get location to write to so we dont screw up the stack
pop es
push bx ; store location for later again
push es
call LoadFAT
.LOAD_IMAGE:
; load the cluster
mov ax, WORD [cluster] ; cluster to read
pop es ; bx:bp=es:bx
pop bx
call ClusterLBA
xor cx, cx
mov cl, BYTE [bpbSectorsPerCluster]
call ReadSectors
pop ecx
inc ecx ; add one more sector to counter
push ecx
push bx
push es
mov ax, FAT_SEG ;start reading from fat
mov es, ax
xor bx, bx
; get next cluster
mov ax, WORD [cluster] ; identify current cluster
mov cx, ax ; copy current cluster
mov dx, ax
shr dx, 0x0001 ; divide by two
add cx, dx ; sum for (3/2)
mov bx, 0 ;location of fat in memory
add bx, cx
mov dx, WORD [es:bx]
test ax, 0x0001 ; test for odd or even cluster
jnz .ODD_CLUSTER
.EVEN_CLUSTER:
and dx, 0000111111111111b ; take low 12 bits
jmp .DONE
.ODD_CLUSTER:
shr dx, 0x0004 ; take high 12 bits
.DONE:
mov WORD [cluster], dx
cmp dx, 0x0ff0 ; test for end of file marker
jb .LOAD_IMAGE
.SUCCESS:
pop es
pop bx
pop ecx
xor ax, ax
ret
%define PMODE_BASE 0x100000
%define RMODE_BASE 0x3000
ImageName db "KRNL SYS"
ImageSize db 0
EnableA20_KKbrd:
cli
push ax
mov al, 0xdd ; send enable a20 address line command to controller
out 0x64, al
pop ax
ret
;--------------------------------------------
; Enables a20 line through output port
;--------------------------------------------
EnableA20_KKbrd_Out:
cli
pusha
call wait_input
mov al,0xAD
out 0x64,al ; disable keyboard
call wait_input
mov al,0xD0
out 0x64,al ; tell controller to read output port
call wait_output
in al,0x60
push eax ; get output port data and store it
call wait_input
mov al,0xD1
out 0x64,al ; tell controller to write output port
call wait_input
pop eax
or al,2 ; set bit 1 (enable a20)
out 0x60,al ; write out data back to the output port
call wait_input
mov al,0xAE ; enable keyboard
out 0x64,al
call wait_input
popa
sti
ret
; wait for input buffer to be clear
wait_input:
in al,0x64
test al,2
jnz wait_input
ret
; wait for output buffer to be clear
wait_output:
in al,0x64
test al,1
jz wait_output
ret
;--------------------------------------
; Enables a20 line through bios
;--------------------------------------
EnableA20_Bios:
pusha
mov ax, 0x2401
int 0x15
popa
ret
;-------------------------------------------------
; Enables a20 line through system control port A
;-------------------------------------------------
EnableA20_SysControlA:
push ax
mov al, 2
out 0x92, al
pop ax
ret
Booting db "Loading Kernel...", 0x0A, 0x0D, 0
Failure db "FATAL ERROR: KRNL.SYS does not exist...", 0x0A, 0x0D, 0
Main:
mov si, Booting
call Print16
cli
xor ax, ax
mov ds, ax
mov es, ax
mov ax, 0x0
mov ss, ax
mov sp, 0xFFFF
sti
SetUp:
call InstallGDT
call EnableA20_KKbrd_Out
call LoadRoot
mov ebx, 0
mov bp, RMODE_BASE
mov si, ImageName
call LoadFile
mov dword [ImageSize], ecx
cmp ax, 0
je StartStage3
mov si, Failure
call Print16
mov ah, 0
int 16
int 19
cli
hlt
StartStage3:
cli
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_DESC:Stage3
bits 32
Stage3:
mov ax, DATA_DESC
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h
CopyImage:
mov eax, dword [ImageSize]
movzx ebx, word [bpbBytesPerSector]
mul ebx
mov ebx, 4
div ebx
cld
mov esi, RMODE_BASE
mov edi, PMODE_BASE
mov ecx, eax
rep movsd
jmp CODE_DESC:PMODE_BASE
cli
hlt
here's GDT.inc
%ifndef __GDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __GDT_INC_67343546FDCC56AAB872_INCLUDED__
bits 16
InstallGDT:
cli
pusha
lgdt [toc]
sti
popa
ret
gdt_data:
dd 0
dd 0
; gdt code:
dw 0FFFFh
dw 0
db 0
db 10011010b
db 11001111b
db 0
; gdt data:
dw 0FFFFh
dw 0
db 0
db 10010010b
db 11001111b
db 0
end_of_gdt:
toc:
dw end_of_gdt - gdt_data - 1
dd gdt_data
%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10
%endif ;__GDT_INC_67343546FDCC56AAB872_INCLUDED__
Here's the build.sh
if test "`whoami`" != "root" ; then
echo "You must be logged in as root to build (for loopback mounting)"
echo "Enter 'su' or 'sudo bash' to switch to root"
exit
fi
if [ ! -e floppy.img ]
then
echo ">>> Creating new floppy image..."
mkdosfs -C floppy.img 1440 || exit
fi
echo ">>> Assembling bootloader..."
nasm Stage1/Stage1.asm -o Stage1.bin || exit
echo ">>> Assembling kernel loader..."
cd Stage2
nasm Stage2.asm -o KRNLDR.SYS || exit
cd
cd Desktop/MyOS
echo ">>> Assembling kernel..."
nasm Kernel/kernel.asm -o KRNL.SYS || exit
echo ">>> Adding bootloader to floppy image..."
dd status=noxfer conv=notrunc if=Stage1.bin of=floppy.img || exit
echo ">>> Copying kernel..."
rm -rf tmp-loop
mkdir tmp-loop && mount -o loop -t vfat floppy.img tmp-loop && cp Stage2/KRNLDR.SYS tmp-loop/ && cp KRNL.SYS tmp-loop/ && cp Stage2/GDT.inc tmp-loop
umount tmp-loop || exit
rm -rf tmp-loop || exit
rm -rf tmp-loop
echo '>>> Done!'