; 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..$';
entry:
; 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 [codesel + 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 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 [es: 0xb8000],0x740 ; put a char to the screen!...yeahh!
; '@' in the top left of the screen if we made it
; here okay.
; 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.
;-------------------------------------------------------------------------
; 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 byte [es: 0xb8004], "B" ; poke a char onto screen (debug)
;-------------------------------------------------------------------------
; 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 byte [es: 0xb8006], "C" ; poke a char onto screen (debug)
;-------------------------------------------------------------------------
; 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)
;-------------------------------------------------------------------------
;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
;-------------------------------------------------------------------------
sti ; Interrupts back..
mov byte [es: 0xb8008], "D" ; poke a char onto screen (debug)
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
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.
;-------------------------------------------------------------------------
|