; Run in dos (not under windows) and it will take us to 32 bit protected mode
[ORG 0x100] ; Reserve 256 bytes for dos
[BITS 16] ; Dos is 16 bits
; assemble using 'nasm' assemblaer
; C:>nasm asm_1.asm -o test.exe
jmp entry ; Jump to the start of our code
msg1 db 'Where good to go..$';
msg2 db '32 Bit Protected Mode!..$',0x0d,0x0;
entry:
; Use the interrupt 0x10 to clear the screen!
mov ah,7 ; scroll down function
mov al,0 ; 0 = entire window
mov cx,0 ; 0,0 as upper left corner.
mov dx,184fh ; 24,79 as lower right corner.
mov bh,7 ; normal attribute
int 10h ; call bios
; Display a message showing where alive!
mov dx, msg1 ; register dx=msg1
mov ah, 9 ; register ah=9 -- the print string function
int 21h ; dos service interrupt .. looks at register ah to figure out what to do
; Thanks from Brendan, as we have to make sure our GDTR points to the actual
; memory address, add code location and dos 0x100 onto our loaded offset
mov eax,0
mov ax,cs
shl eax,4
mov ebx,eax
mov [codesel + 2],ax
mov [datasel + 2],ax
shr eax,16
mov [codesel + 4],al
mov [datasel + 4],al
mov [codesel + 7],ah
mov [datasel + 7],ah
mov eax, ebx
add [gdtr+2],eax
add [idtr+2],eax ; set idtr and gdtr so it points to the 'real' address in memory
;---------------------------------------------------------------------------
cli ; Clear or disable interrupts
mov al, 0x70
mov dx, 0x80
out dx, al ; outb(0x80, 0x70) - disable NMI (Non Maskable Interupts)
lgdt[gdtr] ; Load GDT
lidt[idtr] ; Load IDT
mov eax,cr0 ; The lsb of cr0 is the protected mode bit
or al,0x01 ; Set protected mode bit
mov cr0,eax ; Mov modified word to the control register
jmp 0x8:go_pm ; The 0x8 is so we select our code segment from our gdtr
nop ; ignore - no operation opcodes :)
nop
align 4
;---------------------------------------------------------------------------
; 32 BIT
;---------------------------------------------------------------------------
; Once we reach here where in protected mode! 32 Bit! Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :
xor edi,edi
xor esi,esi
mov ax, 0x10 ; use our datasel selector ( alternatively mov ax, datasel-gdt )
mov ds, ax
mov ss, ax
mov es, ax
mov fs, ax
mov ax, 0x18
mov gs, ax
mov esp, 0afffh ; we need a simple stack if where calling functions!
; Such as interupt functions etc, as the return address is
; put on the stack remember!
mov word [gs: 0xb8000],0x740 ; put a char to the screen!...yeahh!
; '@' in the top left of the screen if we made it
; here okay.
;=========================================================================
; Okay, its worth poking onto the screen a more descriptive message
; that we've made it to the 32 bit world! We use this method of char by
; char poking first, but we'll use our text out functions later...just
; easier to see how it works :)
;=========================================================================
;
; Write '[ 32 bits OK ]' at [gs:0B80A0h].
;
;-------------------------------------------------------------------------
mov byte [gs:0B80A0h], '['
mov byte [gs:0B80A1h], 02h ; Assign a color code
mov byte [gs:0B80A2h], ' '
mov byte [gs:0B80A3h], 02h ; Assign a color code
mov byte [gs:0B80A4h], '3'
mov byte [gs:0B80A5h], 02h ; Assign a color code
mov byte [gs:0B80A6h], '2'
mov byte [gs:0B80A7h], 02h ; Assign a color code
mov byte [gs:0B80A8h], ' '
mov byte [gs:0B80A9h], 02h ; Assign a color code
mov byte [gs:0B80AAh], 'b'
mov byte [gs:0B80ABh], 02h ; Assign a color code
mov byte [gs:0B80ACh], 'i'
mov byte [gs:0B80ADh], 02h ; Assign a color code
mov byte [gs:0B80AEh], 't'
mov byte [gs:0B80AFh], 02h ; Assign a color code
mov byte [gs:0B80B0h], 's'
mov byte [gs:0B80B1h], 02h ; Assign a color code
mov byte [gs:0B80B2h], ' '
mov byte [gs:0B80B3h], 02h ; Assign a color code
mov byte [gs:0B80B4h], 'O'
mov byte [gs:0B80B5h], 02h ; Assign a color code
mov byte [gs:0B80B6h], 'K'
mov byte [gs:0B80B7h], 02h ; Assign a color code
mov byte [gs:0B80B8h], ' '
mov byte [gs:0B80B9h], 02h ; Assign a color code
mov byte [gs:0B80BAh], ' '
mov byte [gs:0B80BBh], 02h ; Assign a color code
mov byte [gs:0B80BCh], ' '
mov byte [gs:0B80BDh], 02h ; Assign a color code
mov byte [gs:0B80BEh], ']'
mov byte [gs:0B80BFh], 02h ; Assign a color code
;=========================================================================
;-------------------------------------------------------------------------
; Simple functions included here
;-------------------------------------------------------------------------
jmp over_includes
%include 'include\text.inc' ; 32-bit. Set default text functions.
msg_basic db 0xd2,0x7,'Working in 32 BIT Mode [ ',0xd2,0x2, ' Loaded OK', 0xd2,0x7, ' ]',0x0d,0x0
msg_irqs_off db "Disabling IRQ's.................", 0x00
msg_remap_pics db "ReMapping the PIC's.............", 0x00
msg_sti_on db "Turning Interrupts back on......", 0x00
msg_loop db "Going into infinite loop........", 0x00
msg_ok db 0xd2,0x2, "[ OK ]", 0xd2,0x7,0xd,0x0
over_includes:
;-------------------------------------------------------------------------
; 0x2 - green
; 0x7 - light grey
; 0x4 - red
mov al, 0x09
call TextColor
mov eax, 0x0
call set_text_pos
call carriage_return
call carriage_return
mov al, 'B'
call print_char
call inc_scr_pointer
mov esi, msg2
call print_string
mov esi, msg_basic
call print_string
; Force a call to interrupt 4!
; Int 0x4 ; We call our interrupt 4 subroutine
;-------------------------------------------------------------------------
; Divide by Zero Warning
;-------------------------------------------------------------------------
; Just remember, when we do a divide by zero, and the interupt is called,
; the return address passed to the interrupt, is in fact the address of the
; line that caused the interrupt! So if we just return from the interrupt
; it would just keep causing the interrupt over and over again.
;-------------------------------------------------------------------------
; Do a divide by 0 error, so we force a call to our interrupt 0
; mov eax, 0
; mov ebx, 0
; div ebx ; eax divided by ebx, and stored back in eax
mov byte [es: 0xb8002], "A" ; poke a character onto our screen buffer
nop
nop
;-------------------------------------------------------------------------
; Where not in basics anymore!
;-------------------------------------------------------------------------
; This is the line where we go from simple settups, to using the computers
; interal hardward, fiddling around with the irqs and pic (Programmable
; Interupt Controller) etc.
;-------------------------------------------------------------------------
mov esi, msg_irqs_off
call print_string
; Disable all IRQs.
;-------------------------------------------------------------------------
disable_irqs:
mov al, 0xFF
mov dx, 0x21
out dx, al ; outb(0x21, 0xFF)
mov al, 0xFF
mov dx, 0xA1
out dx, al ; outb(0x21, 0xFF)
;-------------------------------------------------------------------------
mov esi, msg_ok
call print_string
mov esi, msg_remap_pics
call print_string
;-------------------------------------------------------------------------
; ReMap PICs
;-------------------------------------------------------------------------
; PIC 1 & 2 (Master and Slave)
; Lower 8 IRQs 0x20 onwards
; Higher 8 IRQs 0x28 onwards
;-------------------------------------------------------------------------
remap_pics:
; IWC1
;------
mov al, 0x11
out 0x20, al ; outb(0x20, 0x11)
mov al, 0x11
out 0xA0, al ; outb(0xA0, 0x11)
; IWC2
;------
mov al, 0x20
out 21, al ; outb(0x21, pic1)
mov al, 0x28
out 0xA1, al ; outb(0xA1, pic2)
; IWC3
;------
mov al, 0x04
out 0x21, al ; outb(0x21, 4)
mov al, 0x02
out 0xA1, al ; outb(0xA1, 2)
; IWC4
;------
mov al, 0x01
out 0x21, al ; outb(0x21, 0x01)
mov al, 0x01
out 0xA1, al ; outb(0xA1, 0x01)
;-------------------------------------------------------------------------
mov esi, msg_ok
call print_string
;-------------------------------------------------------------------------
; Every time an irq interupt occurs, we must clear it before another irq
; is sent. Else another interupt wont' be sent till its been cleared.
; We usually call this at the end of our interupt routine.
;EOI for IRQ 0-7
mov al, 0x20
mov dx, 0x20
out dx, al ; outb(0x20, 0x20)
;-------------------------------------------------------------------------
mov esi, msg_sti_on
call print_string
;-------------------------------------------------------------------------
sti ; Interrupts back..
;-------------------------------------------------------------------------
mov esi, msg_ok
call print_string
;-------------------------------------------------------------------------
mov esi, msg_loop
call print_string
lp: jmp lp ; loops here forever and ever...
; Stays in our loop forever, till something happens, such as an interrupt
; is called by pressing a key or the timer calls an interrupt etc
; We use 16 bits here - as you'll notice we use dw and dd only,
; and out data will be packed together nice and tight.
[BITS 16]
align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our GDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdtr :
dw gdt_end-gdt-1 ; Length of the gdt
dd gdt ; physical address of gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
gdt:
gdt0 ; 0x0
dd 0
dd 0
codesel: ; 0x8
dw 0x0ffff
dw 0x0000
db 0x00
db 0x09a
db 0x0cf
db 0x00
datasel: ; 0x10
dw 0x0ffff
dw 0x0000
db 0x00
db 0x092
db 0x0cf
db 0x00
linearsel: ; 0x18
dw 0x0ffff
dw 0x0000
db 0x00
db 0x092
db 0x0cf
db 0x00
gdt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our IDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idtr :
dw idt_end - idt_start - 1 ; Length of the idt
dd idt_start ; physical address of idt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our idt - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;-------------------------------------------------------------------------
[BITS 16]
align 4
idt_start:
%rep 256 ; We have enough room for 256 ISRs
dw intfunc ; offset 15:0
dw 0x0008 ; selector
dw 0x8E00 ; present,ring 0,386 interrupt gate
dw 0 ; offset 31:16
%endrep
idt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;-------------------------------------------------------------------------
; Interrupt Routine
;-------------------------------------------------------------------------
[bits 32]
align 4
intfunc:
pushad
push es
nop
mov ax,0x10
mov es,ax
mov byte [es: 0xb8012], "I" ; poke a character into the graphics output screen
pop es
popad
iret
;-------------------------------------------------------------------------
TIMES 0x1000-($-$$) DB 0x90 ; And of course, this will make our file size
; equal to 0x1000 a nice round number -
; 0x1000 ... so if you assemble the file
; you should find its that size exactly.
;-------------------------------------------------------------------------
|