I know this question gets asked a lot but every answer I find doesn't work for me. I'm trying to load stage 2
of my OS, located at the second sector of my image file(0x200
)
This is the code I tried to use:
bits 16 ; Starting at 16 bits
org 0x0 ; And starting at 0
jmp main ; Hop to main!
; TODO: copy comment from prev. loader
; args: SI
print:
lodsb ; Load the next/first character to AL
or al, al ; Is it 0?
jz donePrint ; Yes - Done.
mov ah, 0eh ; No - keep going.
int 10h ; Print character.
jmp print ; Repeat
donePrint:
ret ; Return
; todo: args
readSector:
mov ah, 02h
mov al, 1
mov dl, 0x80
mov ch, 0
mov dh, 0
mov cl, 2
mov bx, 0x500
int 13h
jnc good
jmp fail
main:
; First, setup some registers.
cli ; Clear interrupts
mov ax, 0x07C0 ; Point all registers to segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Create the stack(0x0000-0xFFFF).
mov ax, 0x0000
mov ss, ax ; Point SS to 0x0000
mov sp, 0xFFFF ; Stack pointer at 0xFFFF
sti ; Restore interrupts
mov si, LOADING
call print
call readSector
fail:
mov si, FAILURE_MSG
call print
good:
mov si, LOADOK
call print
jmp 0x500
LOADING db 0x0D, 0x0A, "Booting loader...", 0x0D, 0x0A, 0x00
FAILURE_MSG db 0x0D, 0x0A, "ERROR: Press any key to reboot.", 0x0A, 0x00
LOADOK db 0x0D, 0x0A, "load ok", 0x0A, 0x00
TIMES 510 - ($-$$) DB 0
DW 0xAA55
But it just bootloops. I tried other solutions to no avail. What am I doing wrong? If I need to update the question please tell me.
Thank you!
EDIT #1: According to Sep Roland's answer, I updated my code, but it is still not working. I'm putting the updated code here if it's any help. Also, if asked for it, I can post my 2nd stage code. It should be using 0x500 as org. NEW CODE:
bits 16 ; Starting at 16 bits
org 0x0 ; And starting at 0
jmp main ; Hop to main!
; TODO: copy comment from prev. loader
; args: SI
print:
lodsb ; Load the next/first character to AL
or al, al ; Is it 0?
jz donePrint ; Yes - Done.
mov ah, 0eh ; No - keep going.
int 10h ; Print character.
jmp print ; Repeat
donePrint:
ret ; Return
; todo: args
readSector:
mov ah, 02h
mov al, 1
mov ch, 0
mov dh, 0
mov cl, 2
mov bx, 0x500
int 13h
jnc good
jmp fail
main:
; First, setup some registers.
cli ; Clear interrupts
mov ax, 0x07C0 ; Point all registers to segment
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
; Create the stack(0x0000-0xFFFF).
mov ax, 0x0000
mov ss, ax ; Point SS to 0x0000
mov sp, 0xFFFE ; Stack pointer at 0xFFFE
sti ; Restore interrupts
mov si, LOADING
call print
call readSector
fail:
mov si, FAILURE_MSG
call print
end:
cli
hlt
jmp end
good:
mov si, LOADOK
call print
jmp 0x07C0:0x0500
LOADING db 0x0D, 0x0A, "Booting loader...", 0x0D, 0x0A, 0x00
FAILURE_MSG db 0x0D, 0x0A, "ERROR: Press any key to reboot.", 0x0A, 0x00
LOADOK db 0x0D, 0x0A, "load ok", 0x0A, 0x00
TIMES 510 - ($-$$) DB 0
DW 0xAA55
EDIT #2: Posting second stage code including gdt.inc
because someone mentioned LGDT
may have been causing a problem:
MAIN CODE(SOME PARTS HAVE BEEN CUT OUT BUT THEY ARE NOT REQUIRED, like strings)
bits 16 ; We start at 16 bits
org 0x500 ; We are loaded in at 0x500
jmp main ; Jump to main code.
; ----------------------------------------
; Includes
; ----------------------------------------
%include "include/stdio.inc"
%include "include/gdt.inc"
%include "include/a20.inc"
; ---------------------------------------
; Data and strings
; ---------------------------------------
stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00
stringhidden db "Not showing string.", 0x0D, 0x0A, 0x00
; ---------------------------------------------------------------------
; main - 16-bit entry point
; Installing GDT, storing BIOS info, and enabling protected mode
; ---------------------------------------------------------------------
main:
; Our goal is jump to main32 to become 32-bit
; Setup segments and stack
cli ; Clear interrupts
xor ax, ax ; Null segments AX, DS, and ES
mov ds, ax
mov es, ax
mov ax, 0x9000 ; Stack begins at 0x9000-0xFFFF
mov ss, ax
mov sp, 0xFFFF ; Stack pointer is 0xFFFF
sti ; Enable interrupts
; Install the GDT
call installGDT ; Install the GDT
; Enable A20
call enableA20_KKbrd_Out ; Enable A20 through output port
; Print loading messages
mov si, msg1
call print16 ; Print the message
mov si, msg2 ; A message
call print16 ; Print the message
; Enter protected mode
cli ; Clear interrupts
mov eax, cr0 ; Set bit 0 in CR0--ENTER protected mode
or eax, 1
mov cr0, eax
jmp CODE_DESC:main32 ; Far jump to fix CS
; We can't re-enable interrupts because that would triple-fault. This will be fixed in main32.
bits 32 ; We are now 32 bit!
%include "include/stdio32.inc"
main32:
; Set registers up
mov ax, 0x10 ; Setup data segments to 0x10(data selector)
mov ds, ax
mov ss, ax
mov es, ax
mov esp, 90000h ; Stack begins from 90000h
call clear32 ; Clear screen
mov ebx, MSGHIDDEN ; Setup params for our message
call puts32 ; Call puts32 to print
cli ; Clear interrupts
hlt ; Halt the processor
LGDT CODE:
%ifndef __GDT_INC_67343546FDCC56AAB872_INCLUDED__
%define __GDT_INC_67343546FDCC56AAB872_INCLUDED__
bits 16 ; We are in 16-bit mode
; -----------------------------------------
; installGDT - install the GDT
; -----------------------------------------
installGDT:
cli ; Clear interrupts
pusha ; Save the registers
lgdt [toc] ; Load GDT into GDTR
sti ; Re-enable interrupts
popa ; Restore registers
ret ; Return!
; ----------------------------------------
; Global Descriptor Table data
; ----------------------------------------
gdt_data:
dd 0 ; Null descriptor
dd 0
; GDT code starts here
dw 0FFFFh ; Limit low
dw 0 ; Base low
db 0 ; Base middle
db 10011010b ; Access
db 11001111b ; Granularity
db 0 ; Base high
; GDT data starts here(mostly same as code, only difference is access)
dw 0FFFFh ; Limit low, again.
dw 0 ; Base low
db 0 ; Base middle
db 10010010b ; Access - different
db 11001111b ; Granularity
db 0
gdt_end:
toc:
dw gdt_end - gdt_data - 1
dd gdt_data ; Base of GDT
; Descriptor offsets names
%define NULL_DESC 0
%define CODE_DESC 0x8
%define DATA_DESC 0x10
; End of GDT code.
%endif ;__GDT_INC_67343546FDCC56AAB872_INCLUDED__
EDIT #3: Possible problems with stdio and stdio32 so putting those here
stdio.inc:
; ==============================================
; stdio.inc - IO routines
; Thanks to BrokenThorn Entertainment
; ==============================================
; First, show that we are defining stdio.inc
%ifndef __STDIO_INC_67343546FDCC56AAB872_INCLUDED__
%define __STDIO_INC_67343546FDCC56AAB872_INCLUDED__
; ------------------------------------------------
; Print16 - printing a null terminated string
; SI - 0 terminated string
; ------------------------------------------------
print16:
pusha ; Save registers for later
.loop1:
lodsb ; Load the next byte from the string into AL
or al, al ; Is AL 0?
jz print16done ; Yes - we are done.
mov ah, 0eh ; No - print next character
int 10h ; Call BIOS
jmp .loop1 ; Repeat!
print16done:
popa ; Restore registers
ret ; Return
%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__
stdio32.inc:
; ==================================================
; stdio32.inc - Handles 32-bit graphics
; ==================================================
%ifndef __GFX_INC_67343546FDCC56AAB872_INCLUDED__
%define __GFX_INC_67343546FDCC56AAB872_INCLUDED__
bits 32 ; 32-bits
%define VIDEO_MEMORY 0xB8000 ; Video memory address
%define COLS 80 ; Width of the screen
%define LINES 25 ; Height of the string
%define CHARACTER_ATTRIBURE 63 ; White text on Cyan background
_CurrentXPos db 0
_CurrentYPos db 0
; ---------------------------------------------------------
; char32 - Print a character to the screen(32-bit)
; BL - Character to print
; ---------------------------------------------------------
char32:
pusha ; Save registers
mov edi, VIDEO_MEMORY ; Get the pointer to the video memory
; Get current position
xor eax, eax ; Zero-out EAX
mov ecx, COLS*2 ; Mode 7 has 2 bytes per character - and so COLS*2 bytes per line.
mov al, byte [_CurrentYPos] ; Get Y position
mul ecx ; Multiply COLS * Y
push eax ; Save EAX--the multiplication
mov al, byte [_CurrentXPos] ; Multiply _CurrentXPos by 2 because 2 bytes per char(Mode 7)
mov cl, 2
mul cl
pop ecx ; Pop Y*COLS result
add eax, ecx
xor ecx, ecx
add edi, eax ; Add to base address
; Watch for a new line!
cmp bl, 0x0A ; 0x0A - newline character.
je .row ; Jump to .row if newline char
; Print the character
mov dl, bl ; Get character
mov dh, CHARACTER_ATTRIBURE ; Change DH to Character Attribute
mov word [edi], dx ; Write to video memory
; Update next pos
inc byte [_CurrentXPos] ; Go to next character
;cmp byte [_CurrentXPos], COLS ; EOL?
;je .row ; Yep - move to next row
jmp .done ; Nope - BAIL!
.row:
; Goto next row.
mov byte [_CurrentXPos], 0 ; Return to col 0
inc byte [_CurrentYPos] ; Go to next row.
.done:
; Return
popa
ret
; ---------------------------------------------------------
; puts32 - print a null terminated string
; EBX - String to print
; ---------------------------------------------------------
puts32:
; Store registers(EBX and EDI)
pusha ; Save registers
push ebx ; Copy string
pop edi
.loop:
mov bl, byte [edi] ; Get next character
cmp bl, 0 ; Check if it's null
je .done ; It is - done printing.
call char32 ; It isn't - print the character
inc edi ; Increment EDI for next character
jmp .loop ; Restart loop
.done:
; Update the hardware cursor
mov bh, byte [_CurrentXPos] ; BH and BL are the params for movecursor
mov bl, byte [_CurrentYPos]
call movecursor ; Update cursor position
popa ; Restore registers
ret ; Return!
bits 32
; ---------------------------------------------------------
; movecursor - Move the cursor to an X and Y position
; BH - X position
; BL - Y position
; ---------------------------------------------------------
movecursor:
pusha ; Save registers
; Get current position(BH and BL are relative to the current position on screen, not memory)
xor eax, eax ; Clear EAX
mov ecx, COLS ; Store COLS in ECX for multiplication
mov al, bh ; Get Y position
mul ecx ; Multiply Y by cols
add al, bl ; 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
; Do the same but for high byte
xor eax, eax
mov al, 0x0e
mov dx, 0x03D4
out dx, al
mov al, bl
mov dx, 0x03D5
out dx, al
popa ; Restore registers
ret ; Return
; ---------------------------------------------------------
; clear32 - clearing the screen
; ---------------------------------------------------------
clear32:
pusha ; Save registers
cld
mov edi, VIDEO_MEMORY ; Set EDI to video memory
mov cx, 2000
mov ah, CHARACTER_ATTRIBURE ; Clear screen with character attribute
mov al, ' ' ; Replace all chars with space
rep stosw
mov byte [_CurrentXPos], 0 ; Reset X and Y position
mov byte [_CurrentYPos], 0
popa ; Restore registers
ret
; ---------------------------------------------------------
; gotoxy - Set X and Y position
; AL - X position
; AH - Y position
; ---------------------------------------------------------
gotoxy:
pusha
mov [_CurrentXPos], al ; Set X and Y position
mov [_CurrentYPos], ah
popa
ret
%endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__