; 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..$';
jumpOffset:
dd go_pm
dw 0x08
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
add [gdtr+2],eax
add [idtr+2],eax ; set idtr and gdtr so it points to the 'real' address in memory
add [jumpOffset],eax ; do the same for our 32 pm addr
add [int_start], eax ; Set the value of int_start (our simple interrupt function) so
; it has the "physical" address
;We have one interrupt function at the bottom of the code:
;divide by zeros, interrupts, irq's ...all will call this same interrupt.
;Our main one will be intfunc, and its address is saved in memory location int_start
mov eax, [int_start]
mov word [idt_start], ax
shr eax,16
mov word [idt_start+6], ax
;Note that we are using this interrupt for testing...later on we can add different
;routines for the different interrupts.
;---------------------------------------------------------------------------
; Whats all this about?...
;---------------------------------------------------------------------------
; We loop through all 46 of the interrupt function addresses in our IDT and
; set them all to point to the same interupt function, which we need to
; correct because dos loads our code at some unknown address in memory and
; our IDT address values need to be the real memory address, not the offsets.
mov ecx, 46 ; Number of IDT Interupt Functions
mov ebx, 0 ; Start value for our loop counter
zloop:
mov eax, [int_start]
add ebx, 8
mov word [idt_start + ebx], ax
shr eax,16
mov word [idt_start+6 + ebx], ax
dec ecx
jne zloop ; if ecx is 0, end of loop
;---------------------------------------------------------------------------
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 far dword [jumpOffset] ;can't just use "jmp go_pm" as where in dos!
nop ; ignore - no operation opcodes :)
nop
;---------------------------------------------------------------------------
; 32 BIT
;---------------------------------------------------------------------------
; Once we reach here where in protected mode! 32 Bit! Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :
mov ax, 0x10 ; use our datasel selector ( alternatively mov ax, datasel )
mov ds, ax,
mov es, ax
mov fs, ax
mov gs, ax
mov ss, 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: 0xb8008], "B" ; 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: 0xb8020], "Q" ; poke a char onto screen (debug)
mov cl, 0x20 ; PIC 1.
mov ch, 0x28 ; PIC 2.
;-------------------------------------------------------------------------
; Remap PICs to: CL = pic1 CH = pic2
;-------------------------------------------------------------------------
remap_pics:
; IWC1
;------
mov al, 0x11
mov dx, 0x20
out dx, al ; outb(0x20, 0x11)
mov al, 0x11
mov dx, 0xA0
out dx, al ; outb(0xA0, 0x11)
; IWC2
;------
mov al, cl
mov dx, 0x21
out dx, al ; outb(0x21, pic1)
mov al, ch
mov dx, 0xA1
out dx, al ; outb(0xA1, pic2)
; IWC3
;------
mov al, 0x04
mov dx, 0x21
out dx, al ; outb(0x21, 4)
mov al, 0x02
mov dx, 0xA1
out dx, al ; outb(0xA1, 2)
; IWC4
;------
mov al, 0x01
mov dx, 0x21
out dx, al ; outb(0x21, 0x01)
mov al, 0x01
mov dx, 0xA1
out dx, al ; outb(0xA1, 0x01)
;-------------------------------------------------------------------------
mov byte [es: 0xb8022], "R" ; poke a char onto screen (debug)
;-------------------------------------------------------------------------
; Enable Keyboard PIC chips 'ONLY', disable others:
;(Port 0x21 bit 1 is keyboard)
mov al, 0xFD
mov dx, 0x21
out dx, al ; outb(0x21, ~0x01)
mov al, 0xFF
mov dx, 0xA1
out dx, al ; outb(0xA1, ~0x00)
mov byte [es: 0xb8024], "S" ; 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: 0xb8026], "T" ; poke a char onto screen (debug)
;int 0
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
dd 0
dd 0
codesel:
dw 0x0ffff
dw 0x0000
db 0x00
db 0x09a
db 0x0cf
db 0x00
datasel:
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
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Nothing special, just a very basic interrupt function so we know something
; has happened
;-------------------------------------------------------------------------
[BITS 16]
;-------------------------------------------------------------------------
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
idt_start:
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 5
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 10
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 15
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 20
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 25
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 29
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 32
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0
dw intfunc, 8, 08e00h, 0 ; 47
idt_end:
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
[bits 32]
int_start :
dd intfunc
dw 0x08
;-------------------
align 4
;________________________________________________________________________
;
; Interrupt Routine
;________________________________________________________________________
intfunc:
;________________________________________________________________________
pushad
push es
;For each key pressed or released an interrupt (INT 09h) is issued by the
;keyboard controller. When the interrupt is issued we can read the value of
;the key pressed/released from the port 60h: if the 7th bit is set the key is
;released, else is pressed. So we can register multiple keys pressions and
;the sequence of the last n key pressed in this way:
;Read the key from the keyboard controller
; last_key=inportb(0x60);
in al,0x60 ; al = inb(0x60)
mov cl, al ; save the character we read from the keyboard
; in cl
;Tell to the controller that the key code is readed
; al=inportb(0x61);
; al|=0x82;
; outportb(0x61,al);
; al&=0x7F;
; outportb(0x61,al);
in al,0x61 ; al = inb(0x61)
or al, 0x82 ; al |=0x82
mov dx, 0x61
out dx, al ; outb(0x61, al)
and al,0x7f ; al&=0x7F;
mov dx, 0x61
out dx, al ; outb(0x61, al)
nop
mov ax,0x10
mov es,ax
; Use the character we got from the keyboard
; and saved in cl, and put it on the screen.
mov byte [es: 0xb8002], cl ; poke a character into the graphics output screen
;EOI for IRQ 0-7
mov al, 0x20
mov dx, 0x20
out dx, al ; outb(0x20, 0x20)
pop es
popad
iret
;-------------------------------------------------------------------------
TIMES 0x500-($-$$) DB 0x90 ; And of course, this will make our file size
; equal to 0x500 a nice round number -
; 0x500 ... so if you assemble the file
; you should find its that size exactly.
;-------------------------------------------------------------------------
|