; 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' assembler
; 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
; Where in dos, and we've done a simple text message to the screen to show
; our program is running... so now where going to break out of this real 16 bit
; world and get into 32 protected mode. So lets set things up and go go go..
cli ; Clear or disable interrupts
; 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 [jumpOffset],eax ; do the same for our 32 pm addr
;-------------------------------------------------------------------------
; NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW *
;-------------------------------------------------------------------------
add [idtr+2],eax ; set idtr and gdtr so it points to the 'real' address in memory
add [int_addr], eax ; Set the value of int_addr (our simple interrupt function) so
; it has the "physical" address
mov eax, [int_addr]
mov word [idt_start], ax ; We have to fix the values in our idt table to they
shr eax,16 ; are set to the real memory address of our interrupt
mov word [idt_start+6], ax ; function
lidt[idtr] ; Load IDT
lgdt[gdtr] ; Load GDT
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, 0abffh ; we need a simple stack if where calling functions!
; To show we've made it this far, lets poke a char into the top
; left of the graphics card memory so it shows up on the screen
mov word [es:0xb8000], 0x742 ; Display white B in protected mode
;------------------------------------------------------------------------
; NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW * NEW *
;------------------------------------------------------------------------
; Force a call to interrupt 0!
; Int 0x0 ; We call our interrupt 0 subroutine
; 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
; Just a note, we don't need to switch back on interrupts with
; the opcode sti to use the cpu interrupts :)
nop
nop
spin : jmp spin ; Loop forever
; We use 16 bit alignment 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 - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
gdt
nullsel equ $-gdt ; $->current location,so nullsel = 0h
gdt0 ; Null descriptor,as per convention gdt0 is 0
dd 0 ; Each gdt entry is 8 bytes, so at 08h it is CS
dd 0 ; In all the segment descriptor is 64 bits
codesel equ $-gdt ; This is 8h,ie 2nd descriptor in gdt
code_gdt ; Code descriptor 4Gb flat segment at 0000:0000h
dw 0x0ffff ; Limit 4Gb bits 0-15 of segment descriptor
dw 0x0000 ; Base 0h bits 16-31 of segment descriptor (sd)
db 0x00 ; Base addr of seg 16-23 of 32bit addr,32-39 of sd
db 0x09a ; P,DPL(2),S,TYPE(3),A->Present bit 1,Descriptor
; privilege level 0-3,Segment descriptor 1 ie code
; or data seg descriptor,Type of seg,Accessed bit
db 0x0cf ; Upper 4 bits G,D,0,AVL ->1 segment len is page
; granular, 1 default operation size is 32bit seg
; AVL : Available field for user or OS
; Lower nibble bits 16-19 of segment limit
db 0x00 ; Base addr of seg 24-31 of 32bit addr,56-63 of sd
datasel equ $-gdt ; ie 10h, beginning of next 8 bytes for data sd
data_gdt ; Data descriptor 4Gb flat seg at 0000:0000h
dw 0x0ffff ; Limit 4Gb
dw 0x0000 ; Base 0000:0000h
db 0x00 ; Descriptor format same as above
db 0x092
db 0x0cf
db 0x00
gdt_end
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;*********************************NEW*************************************
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our IDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idtr :
dw idt_end - idt_start - 1 ; Length of the idt
dd idt_start ; physical address of idt
align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our idt - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
idt_start:
isr_00_interrupt: ;
dw isr_00 ; low word offset (i.e. isr_00 & 0xffff)
dw 0x0008 ; selector
db 0x00 ; control parameters
db 0x8E ; access details
dw 0 ; high word offset
; (won't let us do the isr_00>>16 so we would have
; to do this at run time earlier)
idt_end:
; Offset values to our simple test interrupt
;-------------------------------------------------------------------------
int_addr :
dd isr_00
dw 0x08
[BITS 32]
; Make sure you include this 'bits 32' here else where in 16bit mode!
; and it causes a crash...thanks to Brandon for noticing this :)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; When an interrupt occurs we'll just end up here, calling these
; functions, of course there both the same but we can make them do
; different things later on :)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
isr_00:
isr_20:
pushad
push es
mov ax,0x10
mov es,ax
mov byte [es:0xb8020], "U" ; poke a character into the graphics output screen
pop es
popad
iret
;-------------------------------------------------------------------------
TIMES 0x500-($-$$) DB 0x0 ; 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.
|